1 /*
2 Copyright 2008-2010, Somsak Sriprayoonsakul <somsaks@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Some part of the code is copied from facebook-pidgin protocols.
18 For the facebook-pidgin projects, please see http://code.google.com/p/pidgin-facebookchat/.
19
20 Courtesy to eionrobb at gmail dot com
21 */
22
23 #include <glib.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <glib/gi18n.h>
30 #include <sys/types.h>
31 #include <time.h>
32
33
34 #ifndef G_GNUC_NULL_TERMINATED
35 # if __GNUC__ >= 4
36 # define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
37 # else
38 # define G_GNUC_NULL_TERMINATED
39 # endif /* __GNUC__ >= 4 */
40 #endif /* G_GNUC_NULL_TERMINATED */
41
42 #include <proxy.h>
43 #include <sslconn.h>
44 #include <prpl.h>
45 #include <debug.h>
46 #include <connection.h>
47 #include <request.h>
48 #include <dnsquery.h>
49 #include <accountopt.h>
50 #include <xmlnode.h>
51 #include <version.h>
52 //#include <gtkconv.h>
53
54 #include "mb_net.h"
55 #include "mb_util.h"
56 #include "tw_cmd.h"
57
58 #ifdef _WIN32
59 # include <win32dep.h>
60 #else
61 # include <arpa/inet.h>
62 # include <sys/socket.h>
63 # include <netinet/in.h>
64 #endif
65
66 #include "twitter.h"
67 #include "mb_cache_util.h"
68
69 MbConfig * _mb_conf = NULL;
70
71 static TwCmd * tw_cmd = NULL;
72
73 PurplePlugin * prpl_plugin = NULL;
74
75 void twitterim_remove_oauth(PurplePluginAction * action);
76
plugin_init(PurplePlugin * plugin)77 static void plugin_init(PurplePlugin *plugin)
78 {
79 purple_debug_info("twitterim", "plugin_init\n");
80 purple_debug_info("twitterim", "plugin = %p\n", plugin);
81 purple_signal_register(plugin, "twitter-message",
82 purple_marshal_VOID__POINTER_POINTER_POINTER,
83 NULL, 3,
84 purple_value_new(PURPLE_TYPE_POINTER), // MbAccount ta
85 purple_value_new(PURPLE_TYPE_STRING), // gchar * name
86 purple_value_new(PURPLE_TYPE_POINTER) // TwitterMsg cur_msg
87 );
88 mb_cache_init();
89 }
90
plugin_destroy(PurplePlugin * plugin)91 static void plugin_destroy(PurplePlugin * plugin)
92 {
93 purple_debug_info("twitterim", "plugin_destroy\n");
94 purple_signal_unregister(plugin, "twitter-message");
95 }
96
plugin_load(PurplePlugin * plugin)97 gboolean plugin_load(PurplePlugin *plugin)
98 {
99 PurpleAccountOption *option;
100 PurplePluginInfo *info = plugin->info;
101 PurplePluginProtocolInfo *prpl_info = info->extra_info;
102 GList * auth_type_list = NULL;
103 PurpleKeyValuePair * kv;
104
105 purple_debug_info("twitterim", "plugin_load\n");
106
107 _mb_conf = (MbConfig *)g_malloc0(TC_MAX * sizeof(MbConfig));
108
109 // This is just the place to pass pointer to plug-in itself
110 _mb_conf[TC_PLUGIN].conf = NULL;
111 _mb_conf[TC_PLUGIN].def_str = (gchar *)plugin;
112
113 // Authentication types
114 _mb_conf[TC_AUTH_TYPE].conf = g_strdup("twitter_auth_type");
115 _mb_conf[TC_AUTH_TYPE].def_str = g_strdup(mb_auth_types_str[MB_OAUTH]);
116
117 kv = g_new(PurpleKeyValuePair, 1);
118 kv->key = g_strdup("OAuth");
119 kv->value = g_strdup((char *)mb_auth_types_str[MB_OAUTH]);
120 auth_type_list = g_list_append(auth_type_list, kv);
121
122 kv = g_new(PurpleKeyValuePair, 1);
123 kv->key = g_strdup("XAuth");
124 kv->value = g_strdup((char *)mb_auth_types_str[MB_XAUTH]);
125 auth_type_list = g_list_append(auth_type_list, kv);
126
127 kv = g_new(PurpleKeyValuePair, 1);
128 kv->key = g_strdup("HTTP Basic Authentication (old method)");
129 kv->value = g_strdup((char *)mb_auth_types_str[MB_HTTP_BASICAUTH]);
130 auth_type_list = g_list_append(auth_type_list, kv);
131
132 option = purple_account_option_list_new(_("Authentication Method"), _mb_conf[TC_AUTH_TYPE].conf, auth_type_list);
133 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
134
135 // Hide myself
136 _mb_conf[TC_HIDE_SELF].conf = g_strdup("twitter_hide_myself");
137 _mb_conf[TC_HIDE_SELF].def_bool = TRUE;
138 option = purple_account_option_bool_new(_("Hide myself in conversation"), _mb_conf[TC_HIDE_SELF].conf, _mb_conf[TC_HIDE_SELF].def_bool);
139 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
140
141 _mb_conf[TC_PRIVACY].conf = g_strdup("twitter_privacy");
142 _mb_conf[TC_PRIVACY].def_bool = FALSE;
143 option = purple_account_option_bool_new(_("Not receive messages while unavailable"), _mb_conf[TC_PRIVACY].conf, _mb_conf[TC_PRIVACY].def_bool);
144 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
145
146 _mb_conf[TC_MSG_REFRESH_RATE].conf = g_strdup("twitter_msg_refresh_rate");
147 _mb_conf[TC_MSG_REFRESH_RATE].def_int = 60;
148 option = purple_account_option_int_new(_("Message refresh rate (seconds)"), _mb_conf[TC_MSG_REFRESH_RATE].conf, _mb_conf[TC_MSG_REFRESH_RATE].def_int);
149 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
150
151 _mb_conf[TC_INITIAL_TWEET].conf = g_strdup("twitter_init_tweet");
152 _mb_conf[TC_INITIAL_TWEET].def_int = 15;
153 option = purple_account_option_int_new(_("Number of initial tweets"), _mb_conf[TC_INITIAL_TWEET].conf, _mb_conf[TC_INITIAL_TWEET].def_int);
154 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
155
156 _mb_conf[TC_GLOBAL_RETRY].conf = g_strdup("twitter_global_retry");
157 _mb_conf[TC_GLOBAL_RETRY].def_int = 3 ;
158 option = purple_account_option_int_new(_("Maximum number of retry"), _mb_conf[TC_GLOBAL_RETRY].conf, _mb_conf[TC_GLOBAL_RETRY].def_int);
159 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
160
161 _mb_conf[TC_HOST].conf = g_strdup("twitter_hostname");
162 _mb_conf[TC_HOST].def_str = g_strdup("api.twitter.com");
163 option = purple_account_option_string_new(_("Hostname"), _mb_conf[TC_HOST].conf, _mb_conf[TC_HOST].def_str);
164 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
165
166 _mb_conf[TC_USE_HTTPS].conf = g_strdup("twitter_use_https");
167 _mb_conf[TC_USE_HTTPS].def_bool = TRUE;
168 option = purple_account_option_bool_new(_("Use HTTPS"), _mb_conf[TC_USE_HTTPS].conf, _mb_conf[TC_USE_HTTPS].def_bool);
169 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
170
171 _mb_conf[TC_STATUS_UPDATE].conf = g_strdup("twitter_status_update");
172 _mb_conf[TC_STATUS_UPDATE].def_str = g_strdup("/1/statuses/update.xml");
173 option = purple_account_option_string_new(_("Status update path"), _mb_conf[TC_STATUS_UPDATE].conf, _mb_conf[TC_STATUS_UPDATE].def_str);
174 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
175
176 _mb_conf[TC_VERIFY_PATH].conf = g_strdup("twitter_verify");
177 _mb_conf[TC_VERIFY_PATH].def_str = g_strdup("/1/account/verify_credentials.xml");
178 option = purple_account_option_string_new(_("Account verification path"), _mb_conf[TC_VERIFY_PATH].conf, _mb_conf[TC_VERIFY_PATH].def_str);
179 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
180
181 _mb_conf[TC_FRIENDS_TIMELINE].conf = g_strdup("twitter_friends_timeline");
182 _mb_conf[TC_FRIENDS_TIMELINE].def_str = g_strdup("/1/statuses/home_timeline.xml");
183 option = purple_account_option_string_new(_("Home timeline path"), _mb_conf[TC_FRIENDS_TIMELINE].conf, _mb_conf[TC_FRIENDS_TIMELINE].def_str);
184 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
185
186 _mb_conf[TC_USER_TIMELINE].conf = g_strdup("twitter_user_timeline");
187 _mb_conf[TC_USER_TIMELINE].def_str = g_strdup("/1/statuses/user_timeline.xml");
188 option = purple_account_option_string_new(_("User timeline path"), _mb_conf[TC_USER_TIMELINE].conf, _mb_conf[TC_USER_TIMELINE].def_str);
189 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
190
191 _mb_conf[TC_PUBLIC_TIMELINE].conf = g_strdup("twitter_public_timeline");
192 _mb_conf[TC_PUBLIC_TIMELINE].def_str = g_strdup("/1/statuses/public_timeline.xml");
193 option = purple_account_option_string_new(_("Public timeline path"), _mb_conf[TC_PUBLIC_TIMELINE].conf, _mb_conf[TC_PUBLIC_TIMELINE].def_str);
194 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
195
196 _mb_conf[TC_REPLIES_TIMELINE].conf = g_strdup("twitter_replies_timeline");
197 _mb_conf[TC_REPLIES_TIMELINE].def_str = g_strdup("/1/statuses/mentions.xml");
198 option = purple_account_option_string_new(_("Mentions timeline path"), _mb_conf[TC_REPLIES_TIMELINE].conf, _mb_conf[TC_REPLIES_TIMELINE].def_str);
199 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
200
201 // critical options with no setting page
202 _mb_conf[TC_OAUTH_TOKEN].conf = g_strdup("twitter_oauth_token");
203 _mb_conf[TC_OAUTH_TOKEN].def_str = NULL;
204 _mb_conf[TC_OAUTH_SECRET].conf = g_strdup("twitter_oauth_secret");
205 _mb_conf[TC_OAUTH_SECRET].def_str = NULL;
206
207 // and now for non-option global
208 _mb_conf[TC_FRIENDS_USER].def_str = g_strdup("twitter.com");
209 _mb_conf[TC_REPLIES_USER].def_str = g_strdup("twitter.com");
210 _mb_conf[TC_PUBLIC_USER].def_str = g_strdup("twpublic");
211 _mb_conf[TC_USER_USER].def_str = g_strdup("twuser");
212 _mb_conf[TC_USER_GROUP].def_str = g_strdup("Twitter");
213
214 // OAuth stuff
215 _mb_conf[TC_CONSUMER_KEY].def_str = g_strdup("PCWAdQpyyR12ezp2fVwEhw");
216 _mb_conf[TC_CONSUMER_SECRET].def_str = g_strdup("EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg");
217
218 _mb_conf[TC_REQUEST_TOKEN_URL].conf = g_strdup("twitter_oauth_request_token_url");
219 _mb_conf[TC_REQUEST_TOKEN_URL].def_str = g_strdup("/oauth/request_token");
220 option = purple_account_option_string_new(_("OAuth request token path"), _mb_conf[TC_REQUEST_TOKEN_URL].conf, _mb_conf[TC_REQUEST_TOKEN_URL].def_str);
221 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
222
223 _mb_conf[TC_ACCESS_TOKEN_URL].conf = g_strdup("twitter_oauth_access_token_url");
224 _mb_conf[TC_ACCESS_TOKEN_URL].def_str = g_strdup("/oauth/access_token");
225 option = purple_account_option_string_new(_("OAuth access token path"), _mb_conf[TC_ACCESS_TOKEN_URL].conf, _mb_conf[TC_ACCESS_TOKEN_URL].def_str);
226 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
227
228 _mb_conf[TC_AUTHORIZE_URL].conf = g_strdup("twitter_oauth_authorize_token_url");
229 _mb_conf[TC_AUTHORIZE_URL].def_str = g_strdup("/oauth/authorize");
230 option = purple_account_option_string_new(_("OAuth authorize path"), _mb_conf[TC_AUTHORIZE_URL].conf, _mb_conf[TC_AUTHORIZE_URL].def_str);
231 prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
232
233 // command support
234 tw_cmd = tw_cmd_init(info->id);
235
236 return TRUE;
237 }
238
plugin_unload(PurplePlugin * plugin)239 gboolean plugin_unload(PurplePlugin *plugin)
240 {
241 gint i;
242
243 purple_debug_info("twitterim", "plugin_unload\n");
244
245 tw_cmd_finalize(tw_cmd);
246 tw_cmd = NULL;
247
248 g_free(_mb_conf[TC_CONSUMER_KEY].def_str);
249 g_free(_mb_conf[TC_CONSUMER_SECRET].def_str);
250 g_free(_mb_conf[TC_REQUEST_TOKEN_URL].def_str);
251 g_free(_mb_conf[TC_ACCESS_TOKEN_URL].def_str);
252 g_free(_mb_conf[TC_AUTHORIZE_URL].def_str);
253
254 g_free(_mb_conf[TC_HOST].def_str);
255 g_free(_mb_conf[TC_STATUS_UPDATE].def_str);
256 g_free(_mb_conf[TC_VERIFY_PATH].def_str);
257 g_free(_mb_conf[TC_FRIENDS_TIMELINE].def_str);
258 g_free(_mb_conf[TC_USER_TIMELINE].def_str);
259 g_free(_mb_conf[TC_PUBLIC_TIMELINE].def_str);
260 g_free(_mb_conf[TC_FRIENDS_USER].def_str);
261 g_free(_mb_conf[TC_PUBLIC_USER].def_str);
262 g_free(_mb_conf[TC_USER_USER].def_str);
263 g_free(_mb_conf[TC_USER_GROUP].def_str);
264
265 g_free(_mb_conf[TC_AUTH_TYPE].def_str);
266
267 for(i = 0; i < TC_MAX; i++) {
268 if(_mb_conf[i].conf) {
269 g_free(_mb_conf[i].conf);
270 }
271 }
272
273 g_free(_mb_conf);
274
275 return TRUE;
276 }
277
twitterim_list_icon(PurpleAccount * account,PurpleBuddy * buddy)278 const char * twitterim_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
279 {
280 return "twitter";
281 }
282
twitterim_actions(PurplePlugin * plugin,gpointer context)283 GList * twitterim_actions(PurplePlugin *plugin, gpointer context)
284 {
285 GList *m = NULL;
286 PurplePluginAction *act;
287
288 act = purple_plugin_action_new(_("Remove access credential (Oauth Token)"), twitterim_remove_oauth);
289 m = g_list_append(m, act);
290
291 return m;
292 }
293
twitterim_remove_oauth(PurplePluginAction * action)294 void twitterim_remove_oauth(PurplePluginAction * action) {
295 PurpleConnection * gc = action->context;
296
297 purple_account_remove_setting(gc->account, _mb_conf[TC_OAUTH_TOKEN].conf);
298 purple_account_remove_setting(gc->account, _mb_conf[TC_OAUTH_SECRET].conf);
299 purple_notify_formatted(gc->account, _("Access credential removed"),
300 _("Your access credential was removed"), NULL,
301 _("Access credential will be requested again at the next log-in time"), NULL, NULL);
302 }
303
304 PurplePluginProtocolInfo twitter_prpl_info = {
305 /* options */
306 OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_REGISTER_NOSCREENNAME,
307 NULL, /* user_splits */
308 NULL, /* protocol_options */
309 //NO_BUDDY_ICONS /* icon_spec */
310 { /* icon_spec, a PurpleBuddyIconSpec */
311 "png,jpg,gif", /* format */
312 0, /* min_width */
313 0, /* min_height */
314 50, /* max_width */
315 50, /* max_height */
316 10000, /* max_filesize */
317 PURPLE_ICON_SCALE_DISPLAY, /* scale_rules */
318 },
319 twitterim_list_icon, /* list_icon */
320 NULL, /* list_emblems */
321 twitter_status_text, /* status_text */
322 // twitterim_tooltip_text,/* tooltip_text */
323 NULL,
324 twitter_statuses, /* status_types */
325 NULL, /* blist_node_menu */
326 NULL, /* chat_info */
327 NULL, /* chat_info_defaults */
328 twitter_login, /* login */
329 twitter_close, /* close */
330 twitter_send_im, /* send_im */
331 NULL, /* set_info */
332 // twitterim_send_typing, /* send_typing */
333 NULL,
334 // twitterim_get_info, /* get_info */
335 NULL,
336 twitter_set_status,/* set_status */
337 NULL, /* set_idle */
338 NULL, /* change_passwd */
339 // twitterim_add_buddy, /* add_buddy */
340 NULL,
341 NULL, /* add_buddies */
342 // twitterim_remove_buddy,/* remove_buddy */
343 NULL,
344 NULL, /* remove_buddies */
345 NULL, /* add_permit */
346 NULL, /* add_deny */
347 NULL, /* rem_permit */
348 NULL, /* rem_deny */
349 NULL, /* set_permit_deny */
350 NULL, /* join_chat */
351 NULL, /* reject chat invite */
352 NULL, /* get_chat_name */
353 NULL, /* chat_invite */
354 NULL, /* chat_leave */
355 NULL, /* chat_whisper */
356 NULL, /* chat_send */
357 NULL, /* keepalive */
358 NULL, /* register_user */
359 NULL, /* get_cb_info */
360 NULL, /* get_cb_away */
361 NULL, /* alias_buddy */
362 NULL, /* group_buddy */
363 NULL, /* rename_group */
364 twitter_buddy_free, /* buddy_free */
365 NULL, /* convo_closed */
366 purple_normalize_nocase,/* normalize */
367 NULL, /* set_buddy_icon */
368 NULL, /* remove_group */
369 NULL, /* get_cb_real_name */
370 NULL, /* set_chat_topic */
371 NULL, /* find_blist_chat */
372 NULL, /* roomlist_get_list */
373 NULL, /* roomlist_cancel */
374 NULL, /* roomlist_expand_category */
375 NULL, /* can_receive_file */
376 NULL, /* send_file */
377 NULL, /* new_xfer */
378 NULL, /* offline_message */
379 NULL, /* whiteboard_prpl_ops */
380 NULL, /* send_raw */
381 NULL, /* roomlist_room_serialize */
382 NULL, /* unregister_user */
383 NULL, /* send_attention */
384 NULL, /* attention_types */
385 sizeof(PurplePluginProtocolInfo) /* struct_size */
386 };
387
388 static PurplePluginInfo info = {
389 PURPLE_PLUGIN_MAGIC,
390 PURPLE_MAJOR_VERSION,
391 PURPLE_MINOR_VERSION,
392 PURPLE_PLUGIN_PROTOCOL, /* type */
393 NULL, /* ui_requirement */
394 0, /* flags */
395 NULL, /* dependencies */
396 PURPLE_PRIORITY_DEFAULT, /* priority */
397 "prpl-mbpurple-twitter", /* id */
398 "TwitterIM", /* name */
399 MBPURPLE_VERSION, /* version */
400 "Twitter data feeder", /* summary */
401 "Twitter data feeder", /* description */
402 "Somsak Sriprayoonsakul <somsaks@gmail.com>", /* author */
403 "http://microblog-purple.googlecode.com/", /* homepage */
404 plugin_load, /* load */
405 plugin_unload, /* unload */
406 plugin_destroy, /* destroy */
407 NULL, /* ui_info */
408 &twitter_prpl_info, /* extra_info */
409 NULL, /* prefs_info */
410 twitterim_actions, /* actions */
411 NULL, /* padding */
412 NULL,
413 NULL,
414 NULL
415 };
416
417 PURPLE_INIT_PLUGIN(twitterim, plugin_init, info);
418