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