Mercurial > pidgin.yaz
comparison libpurple/protocols/null/nullprpl.c @ 17453:afee8b840d91
this patch adds nullprpl, a mock protocol plugin, to the libpurple/protocols
directory
Closes #811
committer: Richard Laager <rlaager@wiktel.com>
author | Ryan Barrett <ryanbarrett@users.sourceforge.net> |
---|---|
date | Wed, 30 May 2007 01:21:36 +0000 |
parents | |
children | 29137f15743d |
comparison
equal
deleted
inserted
replaced
17447:1a1d9cf5cd79 | 17453:afee8b840d91 |
---|---|
1 /** | |
2 * purple | |
3 * | |
4 * Purple is the legal property of its developers, whose names are too numerous | |
5 * to list here. Please refer to the COPYRIGHT file distributed with this | |
6 * source distribution. | |
7 * | |
8 * Nullprpl is a mock protocol plugin for Pidgin and libpurple. You can create | |
9 * accounts with it, sign on and off, add buddies, and send and receive IMs, | |
10 * all without connecting to a server! | |
11 * | |
12 * Beyond that basic functionality, nullprpl supports presence and | |
13 * away/available messages, offline messages, user info, typing notification, | |
14 * privacy allow/block lists, chat rooms, whispering, room lists, and protocol | |
15 * icons and emblems. Notable missing features are file transfer and account | |
16 * registration and authentication. | |
17 * | |
18 * Nullprpl is intended as an example of how to write a libpurple protocol | |
19 * plugin. It doesn't contain networking code or an event loop, but it does | |
20 * demonstrate how to use the libpurple API to do pretty much everything a prpl | |
21 * might need to do. | |
22 * | |
23 * Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other | |
24 * libpurple clients. It's a full-featured protocol plugin, but doesn't depend | |
25 * on an external server, so it's a quick and easy way to exercise test new | |
26 * code. It also allows you to work while you're disconnected. | |
27 * | |
28 * This program is free software; you can redistribute it and/or modify | |
29 * it under the terms of the GNU General Public License as published by | |
30 * the Free Software Foundation; either version 2 of the License, or | |
31 * (at your option) any later version. | |
32 * | |
33 * This program is distributed in the hope that it will be useful, | |
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
36 * GNU General Public License for more details. | |
37 * | |
38 * You should have received a copy of the GNU General Public License | |
39 * along with this program; if not, write to the Free Software | |
40 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
41 */ | |
42 | |
43 #include <stdarg.h> | |
44 #include <string.h> | |
45 #include <time.h> | |
46 | |
47 #include <glib.h> | |
48 | |
49 #include "internal.h" | |
50 #include "config.h" | |
51 #include "account.h" | |
52 #include "accountopt.h" | |
53 #include "blist.h" | |
54 #include "cmds.h" | |
55 #include "conversation.h" | |
56 #include "connection.h" | |
57 #include "debug.h" | |
58 #include "notify.h" | |
59 #include "privacy.h" | |
60 #include "prpl.h" | |
61 #include "roomlist.h" | |
62 #include "status.h" | |
63 #include "util.h" | |
64 #include "version.h" | |
65 | |
66 | |
67 #define NULLPRPL_ID "prpl-null" | |
68 static PurplePlugin *_null_protocol = NULL; | |
69 | |
70 #define NULL_STATUS_ONLINE "online" | |
71 #define NULL_STATUS_AWAY "away" | |
72 #define NULL_STATUS_OFFLINE "offline" | |
73 | |
74 typedef void (*GcFunc)(PurpleConnection *from, | |
75 PurpleConnection *to, | |
76 gpointer userdata); | |
77 | |
78 typedef struct { | |
79 GcFunc fn; | |
80 PurpleConnection *from; | |
81 gpointer userdata; | |
82 } GcFuncData; | |
83 | |
84 /* | |
85 * stores offline messages that haven't been delivered yet. maps username | |
86 * (char *) to GList * of GOfflineMessages. initialized in nullprpl_init. | |
87 */ | |
88 GHashTable* goffline_messages = NULL; | |
89 | |
90 typedef struct { | |
91 char *from; | |
92 char *message; | |
93 time_t mtime; | |
94 PurpleMessageFlags flags; | |
95 } GOfflineMessage; | |
96 | |
97 /* | |
98 * helpers | |
99 */ | |
100 static PurpleConnection *get_nullprpl_gc(const char *username) { | |
101 PurpleAccount *acct = purple_accounts_find(username, NULLPRPL_ID); | |
102 if (acct && purple_account_is_connected(acct)) | |
103 return acct->gc; | |
104 else | |
105 return NULL; | |
106 } | |
107 | |
108 static void call_if_nullprpl(gpointer data, gpointer userdata) { | |
109 PurpleConnection *gc = (PurpleConnection *)(data); | |
110 GcFuncData *gcfdata = (GcFuncData *)userdata; | |
111 | |
112 if (!strcmp(gc->account->protocol_id, NULLPRPL_ID)) | |
113 gcfdata->fn(gcfdata->from, gc, gcfdata->userdata); | |
114 } | |
115 | |
116 static void foreach_nullprpl_gc(GcFunc fn, PurpleConnection *from, | |
117 gpointer userdata) { | |
118 GcFuncData gcfdata = { .fn = fn, | |
119 .from = from, | |
120 .userdata = userdata }; | |
121 g_list_foreach(purple_connections_get_all(), call_if_nullprpl, | |
122 (gpointer)&gcfdata); | |
123 } | |
124 | |
125 | |
126 typedef void(*ChatFunc)(PurpleConvChat *from, PurpleConvChat *to, | |
127 int id, const char *room, gpointer userdata); | |
128 | |
129 typedef struct { | |
130 ChatFunc fn; | |
131 PurpleConvChat *from_chat; | |
132 gpointer userdata; | |
133 } ChatFuncData; | |
134 | |
135 static void call_chat_func(gpointer data, gpointer userdata) { | |
136 PurpleConnection *to = (PurpleConnection *)data; | |
137 ChatFuncData *cfdata = (ChatFuncData *)userdata; | |
138 | |
139 int id = cfdata->from_chat->id; | |
140 PurpleConversation *conv = purple_find_chat(to, id); | |
141 if (conv) { | |
142 PurpleConvChat *chat = purple_conversation_get_chat_data(conv); | |
143 cfdata->fn(cfdata->from_chat, chat, id, conv->name, cfdata->userdata); | |
144 } | |
145 } | |
146 | |
147 static void foreach_gc_in_chat(ChatFunc fn, PurpleConnection *from, | |
148 int id, gpointer userdata) { | |
149 PurpleConversation *conv = purple_find_chat(from, id); | |
150 ChatFuncData cfdata = { .fn = fn, | |
151 .from_chat = purple_conversation_get_chat_data(conv), | |
152 .userdata = userdata }; | |
153 | |
154 g_list_foreach(purple_connections_get_all(), call_chat_func, | |
155 (gpointer)&cfdata); | |
156 } | |
157 | |
158 | |
159 static void discover_status(PurpleConnection *from, PurpleConnection *to, | |
160 gpointer userdata) { | |
161 char *from_username = from->account->username; | |
162 char *to_username = to->account->username; | |
163 | |
164 if (purple_find_buddy(from->account, to_username)) { | |
165 PurpleStatus *status = purple_account_get_active_status(to->account); | |
166 const char *status_id = purple_status_get_id(status); | |
167 const char *message = purple_status_get_attr_string(status, "message"); | |
168 | |
169 if (!strcmp(status_id, NULL_STATUS_ONLINE) || | |
170 !strcmp(status_id, NULL_STATUS_AWAY) || | |
171 !strcmp(status_id, NULL_STATUS_OFFLINE)) { | |
172 purple_debug_info("nullprpl", "%s sees that %s is %s: %s\n", | |
173 from_username, to_username, status_id, message); | |
174 purple_prpl_got_user_status(from->account, to_username, status_id, | |
175 (message) ? "message" : NULL, message, NULL); | |
176 } else { | |
177 purple_debug_error("nullprpl", | |
178 "%s's buddy %s has an unknown status: %s, %s", | |
179 from_username, to_username, status_id, message); | |
180 } | |
181 } | |
182 } | |
183 | |
184 static void report_status_change(PurpleConnection *from, PurpleConnection *to, | |
185 gpointer userdata) { | |
186 purple_debug_info("nullprpl", "notifying %s that %s changed status\n", | |
187 to->account->username, from->account->username); | |
188 discover_status(to, from, NULL); | |
189 } | |
190 | |
191 | |
192 /* | |
193 * UI callbacks | |
194 */ | |
195 static void nullprpl_input_user_info(PurplePluginAction *action) | |
196 { | |
197 PurpleConnection *gc = (PurpleConnection *)action->context; | |
198 PurpleAccount *acct = purple_connection_get_account(gc); | |
199 purple_debug_info("nullprpl", "showing 'Set User Info' dialog for %s\n", | |
200 acct->username); | |
201 | |
202 purple_account_request_change_user_info(acct); | |
203 } | |
204 | |
205 /* this is set to the actions member of the PurplePluginInfo struct at the | |
206 * bottom. | |
207 */ | |
208 static GList *nullprpl_actions(PurplePlugin *plugin, gpointer context) | |
209 { | |
210 PurplePluginAction *action = purple_plugin_action_new( | |
211 _("Set User Info..."), nullprpl_input_user_info); | |
212 return g_list_append(NULL, action); | |
213 } | |
214 | |
215 | |
216 /* | |
217 * prpl functions | |
218 */ | |
219 static const char *nullprpl_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) | |
220 { | |
221 /* shamelessly steal (er, borrow) the meanwhile protocol icon. it's cute! */ | |
222 return "meanwhile"; | |
223 } | |
224 | |
225 static const char *nullprpl_list_emblem(PurpleBuddy *buddy) | |
226 { | |
227 const char* emblem; | |
228 | |
229 if (get_nullprpl_gc(buddy->name)) { | |
230 PurplePresence *presence = purple_buddy_get_presence(buddy); | |
231 PurpleStatus *status = purple_presence_get_active_status(presence); | |
232 emblem = purple_status_get_name(status); | |
233 } else { | |
234 emblem = "offline"; | |
235 } | |
236 | |
237 purple_debug_info("nullprpl", "using emblem %s for %s's buddy %s\n", | |
238 emblem, buddy->account->username, buddy->name); | |
239 return emblem; | |
240 } | |
241 | |
242 static char *nullprpl_status_text(PurpleBuddy *buddy) { | |
243 purple_debug_info("nullprpl", "getting %s's status text for %s\n", | |
244 buddy->name, buddy->account->username); | |
245 | |
246 if (purple_find_buddy(buddy->account, buddy->name)) { | |
247 PurplePresence *presence = purple_buddy_get_presence(buddy); | |
248 PurpleStatus *status = purple_presence_get_active_status(presence); | |
249 const char *name = purple_status_get_name(status); | |
250 const char *message = purple_status_get_attr_string(status, "message"); | |
251 | |
252 char *text; | |
253 if (message && strlen(message) > 0) | |
254 text = g_strdup_printf("%s: %s", name, message); | |
255 else | |
256 text = g_strdup(name); | |
257 | |
258 purple_debug_info("nullprpl", "%s's status text is %s\n", buddy->name, text); | |
259 return text; | |
260 | |
261 } else { | |
262 purple_debug_info("nullprpl", "...but %s is not logged in\n", buddy->name); | |
263 return "Not logged in"; | |
264 } | |
265 } | |
266 | |
267 static void nullprpl_tooltip_text(PurpleBuddy *buddy, | |
268 PurpleNotifyUserInfo *info, | |
269 gboolean full) { | |
270 PurpleConnection *gc = get_nullprpl_gc(buddy->name); | |
271 | |
272 if (gc) { | |
273 /* they're logged in */ | |
274 PurplePresence *presence = purple_buddy_get_presence(buddy); | |
275 PurpleStatus *status = purple_presence_get_active_status(presence); | |
276 const char *msg = nullprpl_status_text(buddy); | |
277 purple_notify_user_info_add_pair(info, purple_status_get_name(status), | |
278 msg); | |
279 | |
280 if (full) { | |
281 const char *user_info = purple_account_get_user_info(gc->account); | |
282 if (user_info) | |
283 purple_notify_user_info_add_pair(info, _("User info"), user_info); | |
284 } | |
285 | |
286 } else { | |
287 /* they're not logged in */ | |
288 purple_notify_user_info_add_pair(info, _("User info"), _("not logged in")); | |
289 } | |
290 | |
291 purple_debug_info("nullprpl", "showing %s tooltip for %s\n", | |
292 (full) ? "full" : "short", buddy->name); | |
293 } | |
294 | |
295 static GList *nullprpl_status_types(PurpleAccount *acct) | |
296 { | |
297 GList *types = NULL; | |
298 PurpleStatusType *type; | |
299 | |
300 purple_debug_info("nullprpl", "returning status types for %s: %s, %s, %s\n", | |
301 acct->username, | |
302 NULL_STATUS_ONLINE, NULL_STATUS_AWAY, NULL_STATUS_OFFLINE); | |
303 | |
304 type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL_STATUS_ONLINE, | |
305 NULL_STATUS_ONLINE, TRUE); | |
306 purple_status_type_add_attr(type, "message", _("Online"), | |
307 purple_value_new(PURPLE_TYPE_STRING)); | |
308 types = g_list_append(types, type); | |
309 | |
310 type = purple_status_type_new(PURPLE_STATUS_AWAY, NULL_STATUS_AWAY, | |
311 NULL_STATUS_AWAY, TRUE); | |
312 purple_status_type_add_attr(type, "message", _("Away"), | |
313 purple_value_new(PURPLE_TYPE_STRING)); | |
314 types = g_list_append(types, type); | |
315 | |
316 type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL_STATUS_OFFLINE, | |
317 NULL_STATUS_OFFLINE, TRUE); | |
318 purple_status_type_add_attr(type, "message", _("Offline"), | |
319 purple_value_new(PURPLE_TYPE_STRING)); | |
320 types = g_list_append(types, type); | |
321 | |
322 return types; | |
323 } | |
324 | |
325 static void blist_example_menu_item(PurpleBlistNode *node, gpointer userdata) { | |
326 purple_debug_info("nullprpl", "example menu item clicked on user", | |
327 ((PurpleBuddy *)node)->name); | |
328 | |
329 purple_notify_info(NULL, /* plugin handle or PurpleConnection */ | |
330 _("Primary title"), | |
331 _("Secondary title"), | |
332 _("This is the callback for the nullprpl menu item.")); | |
333 } | |
334 | |
335 static GList *nullprpl_blist_node_menu(PurpleBlistNode *node) { | |
336 purple_debug_info("nullprpl", "providing buddy list context menu item\n"); | |
337 | |
338 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { | |
339 PurpleMenuAction *action = purple_menu_action_new( | |
340 _("Nullprpl example menu item"), | |
341 PURPLE_CALLBACK(blist_example_menu_item), | |
342 NULL, /* userdata passed to the callback */ | |
343 NULL); /* child menu items */ | |
344 return g_list_append(NULL, action); | |
345 } else { | |
346 return NULL; | |
347 } | |
348 } | |
349 | |
350 static GList *nullprpl_chat_info(PurpleConnection *gc) { | |
351 struct proto_chat_entry *pce; /* defined in prpl.h */ | |
352 | |
353 purple_debug_info("nullprpl", "returning chat setting 'room'\n"); | |
354 | |
355 pce = g_new0(struct proto_chat_entry, 1); | |
356 pce->label = _(_("Chat _room")); | |
357 pce->identifier = "room"; | |
358 pce->required = TRUE; | |
359 | |
360 return g_list_append(NULL, pce); | |
361 } | |
362 | |
363 static GHashTable *nullprpl_chat_info_defaults(PurpleConnection *gc, | |
364 const char *room) { | |
365 GHashTable *defaults; | |
366 | |
367 purple_debug_info("nullprpl", "returning chat default setting " | |
368 "'room' = 'default'\n"); | |
369 | |
370 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); | |
371 g_hash_table_insert(defaults, "room", g_strdup("default")); | |
372 return defaults; | |
373 } | |
374 | |
375 static void nullprpl_login(PurpleAccount *acct) | |
376 { | |
377 PurpleConnection *gc = purple_account_get_connection(acct); | |
378 GList *offline_messages; | |
379 | |
380 purple_debug_info("nullprpl", "logging in %s\n", acct->username); | |
381 | |
382 purple_connection_update_progress(gc, _("Connecting"), | |
383 0, /* which connection step this is */ | |
384 2); /* total number of steps */ | |
385 | |
386 purple_connection_update_progress(gc, _("Connected"), | |
387 1, /* which connection step this is */ | |
388 2); /* total number of steps */ | |
389 purple_connection_set_state(gc, PURPLE_CONNECTED); | |
390 | |
391 /* tell purple about everyone on our buddy list who's connected */ | |
392 foreach_nullprpl_gc(discover_status, gc, NULL); | |
393 | |
394 /* notify other nullprpl accounts */ | |
395 foreach_nullprpl_gc(report_status_change, gc, NULL); | |
396 | |
397 /* fetch stored offline messages */ | |
398 purple_debug_info("nullprpl", "checking for offline messages for %s\n", | |
399 acct->username); | |
400 offline_messages = g_hash_table_lookup(goffline_messages, acct->username); | |
401 while (offline_messages) { | |
402 GOfflineMessage *message = (GOfflineMessage *)offline_messages->data; | |
403 purple_debug_info("nullprpl", "delivering offline message to %s: %s\n", | |
404 acct->username, message->message); | |
405 serv_got_im(gc, message->from, message->message, message->flags, | |
406 message->mtime); | |
407 offline_messages = g_list_next(offline_messages); | |
408 | |
409 g_free(message->from); | |
410 g_free(message->message); | |
411 g_free(message); | |
412 } | |
413 | |
414 g_list_free(offline_messages); | |
415 g_hash_table_remove(goffline_messages, &acct->username); | |
416 } | |
417 | |
418 static void nullprpl_close(PurpleConnection *gc) | |
419 { | |
420 /* notify other nullprpl accounts */ | |
421 foreach_nullprpl_gc(report_status_change, gc, NULL); | |
422 } | |
423 | |
424 static int nullprpl_send_im(PurpleConnection *gc, const char *who, | |
425 const char *message, PurpleMessageFlags flags) | |
426 { | |
427 const char *from_username = gc->account->username; | |
428 PurpleMessageFlags receive_flags = ((flags & ~PURPLE_MESSAGE_SEND) | |
429 | PURPLE_MESSAGE_RECV); | |
430 PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID); | |
431 PurpleConnection *to; | |
432 | |
433 purple_debug_info("nullprpl", "sending message from %s to %s: %s\n", | |
434 from_username, who, message); | |
435 | |
436 /* is the sender blocked by the recipient's privacy settings? */ | |
437 if (!purple_privacy_check(to_acct, gc->account->username)) { | |
438 char *msg = g_strdup_printf( | |
439 _("Your message was blocked by %s's privacy settings."), who); | |
440 purple_debug_info("nullprpl", | |
441 "discarding; %s is blocked by %s's privacy settings\n", | |
442 from_username, who); | |
443 purple_conv_present_error(who, gc->account, msg); | |
444 g_free(msg); | |
445 return 0; | |
446 } | |
447 | |
448 /* is the recipient online? */ | |
449 to = get_nullprpl_gc(who); | |
450 if (to) { /* yes, send */ | |
451 serv_got_im(to, from_username, message, receive_flags, time(NULL)); | |
452 | |
453 } else { /* nope, store as an offline message */ | |
454 GOfflineMessage *offline_message; | |
455 GList *messages; | |
456 | |
457 purple_debug_info("nullprpl", | |
458 "%s is offline, sending as offline message\n", who); | |
459 offline_message = g_new0(GOfflineMessage, 1); | |
460 offline_message->from = g_strdup(from_username); | |
461 offline_message->message = g_strdup(message); | |
462 offline_message->mtime = time(NULL); | |
463 offline_message->flags = receive_flags; | |
464 | |
465 messages = g_hash_table_lookup(goffline_messages, who); | |
466 messages = g_list_append(messages, offline_message); | |
467 g_hash_table_insert(goffline_messages, g_strdup(who), messages); | |
468 } | |
469 | |
470 return 1; | |
471 } | |
472 | |
473 static void nullprpl_set_info(PurpleConnection *gc, const char *info) { | |
474 purple_debug_info("nullprpl", "setting %s's user info to %s\n", | |
475 gc->account->username, info); | |
476 } | |
477 | |
478 static char *typing_state_to_string(PurpleTypingState typing) { | |
479 switch (typing) { | |
480 case PURPLE_NOT_TYPING: return "is not typing"; | |
481 case PURPLE_TYPING: return "is typing"; | |
482 case PURPLE_TYPED: return "stopped typing momentarily"; | |
483 default: return "unknown typing state"; | |
484 } | |
485 } | |
486 | |
487 static void notify_typing(PurpleConnection *from, PurpleConnection *to, | |
488 gpointer typing) { | |
489 char *from_username = from->account->username; | |
490 char *action = typing_state_to_string((PurpleTypingState)typing); | |
491 purple_debug_info("nullprpl", "notifying %s that %s %s\n", | |
492 to->account->username, from_username, action); | |
493 | |
494 serv_got_typing(to, | |
495 from_username, | |
496 0, /* if non-zero, a timeout in seconds after which to | |
497 * reset the typing status to PURPLE_NOT_TYPING */ | |
498 (PurpleTypingState)typing); | |
499 } | |
500 | |
501 static unsigned int nullprpl_send_typing(PurpleConnection *gc, const char *name, | |
502 PurpleTypingState typing) { | |
503 purple_debug_info("nullprpl", "%s %s\n", gc->account->username, | |
504 typing_state_to_string(typing)); | |
505 foreach_nullprpl_gc(notify_typing, gc, (gpointer)typing); | |
506 return 0; | |
507 } | |
508 | |
509 static void nullprpl_get_info(PurpleConnection *gc, const char *username) { | |
510 const char *body; | |
511 PurpleNotifyUserInfo *info = purple_notify_user_info_new(); | |
512 PurpleAccount *acct; | |
513 | |
514 purple_debug_info("nullprpl", "Fetching %s's user info for %s\n", username, | |
515 gc->account->username); | |
516 | |
517 if (!get_nullprpl_gc(username)) { | |
518 char *msg = g_strdup_printf(_("%s is not logged in."), username); | |
519 purple_notify_error(gc, _("User Info"), _("User info not available. "), msg); | |
520 g_free(msg); | |
521 } | |
522 | |
523 acct = purple_accounts_find(username, NULLPRPL_ID); | |
524 if (acct) | |
525 body = purple_account_get_user_info(acct); | |
526 else | |
527 body = _("No user info."); | |
528 purple_notify_user_info_add_pair(info, "Info", body); | |
529 | |
530 /* show a buddy's user info in a nice dialog box */ | |
531 purple_notify_userinfo(gc, /* connection the buddy info came through */ | |
532 username, /* buddy's username */ | |
533 info, /* body */ | |
534 NULL, /* callback called when dialog closed */ | |
535 NULL); /* userdata for callback */ | |
536 } | |
537 | |
538 static void nullprpl_set_status(PurpleAccount *acct, PurpleStatus *status) { | |
539 const char *msg = purple_status_get_attr_string(status, "message"); | |
540 purple_debug_info("nullprpl", "setting %s's status to %s: %s\n", | |
541 acct->username, purple_status_get_name(status), msg); | |
542 | |
543 foreach_nullprpl_gc(report_status_change, get_nullprpl_gc(acct->username), | |
544 NULL); | |
545 } | |
546 | |
547 static void nullprpl_set_idle(PurpleConnection *gc, int idletime) { | |
548 purple_debug_info("nullprpl", | |
549 "purple reports that %s has been idle for %d seconds\n", | |
550 gc->account->username, idletime); | |
551 } | |
552 | |
553 static void nullprpl_change_passwd(PurpleConnection *gc, const char *old_pass, | |
554 const char *new_pass) { | |
555 purple_debug_info("nullprpl", "%s wants to change their password\n", | |
556 gc->account->username); | |
557 } | |
558 | |
559 static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, | |
560 PurpleGroup *group) | |
561 { | |
562 char *username = gc->account->username; | |
563 PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name); | |
564 | |
565 purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", buddy->name, | |
566 username); | |
567 | |
568 if (buddy_gc) { | |
569 PurpleAccount *buddy_acct = buddy_gc->account; | |
570 | |
571 discover_status(gc, buddy_gc, NULL); | |
572 | |
573 if (purple_find_buddy(buddy_acct, username)) { | |
574 purple_debug_info("nullprpl", "%s is already on %s's buddy list\n", | |
575 username, buddy->name); | |
576 } else { | |
577 purple_debug_info("nullprpl", "asking %s if they want to add %s\n", | |
578 buddy->name, username); | |
579 purple_account_request_add(buddy_acct, | |
580 username, | |
581 NULL, /* local account id (rarely used) */ | |
582 NULL, /* alias */ | |
583 NULL); /* message */ | |
584 } | |
585 } | |
586 } | |
587 | |
588 static void nullprpl_add_buddies(PurpleConnection *gc, GList *buddies, | |
589 GList *groups) { | |
590 GList *buddy = buddies; | |
591 GList *group = groups; | |
592 | |
593 purple_debug_info("nullprpl", "adding multiple buddies\n"); | |
594 | |
595 while (buddy && group) { | |
596 nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data); | |
597 buddy = g_list_next(buddy); | |
598 group = g_list_next(group); | |
599 } | |
600 } | |
601 | |
602 static void nullprpl_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, | |
603 PurpleGroup *group) | |
604 { | |
605 purple_debug_info("nullprpl", "removing %s from %s's buddy list\n", | |
606 buddy->name, gc->account->username); | |
607 } | |
608 | |
609 static void nullprpl_remove_buddies(PurpleConnection *gc, GList *buddies, | |
610 GList *groups) { | |
611 GList *buddy = buddies; | |
612 GList *group = groups; | |
613 | |
614 purple_debug_info("nullprpl", "removing multiple buddies\n"); | |
615 | |
616 while (buddy && group) { | |
617 nullprpl_remove_buddy(gc, (PurpleBuddy *)buddy->data, | |
618 (PurpleGroup *)group->data); | |
619 buddy = g_list_next(buddy); | |
620 group = g_list_next(group); | |
621 } | |
622 } | |
623 | |
624 /* | |
625 * nullprpl uses purple's local whitelist and blacklist, stored in blist.xml, as | |
626 * its authoritative privacy settings, and uses purple's logic (specifically | |
627 * purple_privacy_check(), from privacy.h), to determine whether messages are | |
628 * allowed or blocked. | |
629 */ | |
630 static void nullprpl_add_permit(PurpleConnection *gc, const char *name) { | |
631 purple_debug_info("nullprpl", "%s adds %s to their allowed list\n", | |
632 gc->account->username, name); | |
633 } | |
634 | |
635 static void nullprpl_add_deny(PurpleConnection *gc, const char *name) { | |
636 purple_debug_info("nullprpl", "%s adds %s to their blocked list\n", | |
637 gc->account->username, name); | |
638 } | |
639 | |
640 static void nullprpl_rem_permit(PurpleConnection *gc, const char *name) { | |
641 purple_debug_info("nullprpl", "%s removes %s from their allowed list\n", | |
642 gc->account->username, name); | |
643 } | |
644 | |
645 static void nullprpl_rem_deny(PurpleConnection *gc, const char *name) { | |
646 purple_debug_info("nullprpl", "%s removes %s from their blocked list\n", | |
647 gc->account->username, name); | |
648 } | |
649 | |
650 static void nullprpl_set_permit_deny(PurpleConnection *gc) { | |
651 /* this is for synchronizing the local black/whitelist with the server. | |
652 * for nullprpl, it's a noop. | |
653 */ | |
654 } | |
655 | |
656 static void joined_chat(PurpleConvChat *from, PurpleConvChat *to, | |
657 int id, const char *room, gpointer userdata) { | |
658 /* tell their chat window that we joined */ | |
659 purple_debug_info("nullprpl", "%s sees that %s joined chat room %s\n", | |
660 to->nick, from->nick, room); | |
661 purple_conv_chat_add_user(to, | |
662 from->nick, | |
663 NULL, /* user-provided join message, IRC style */ | |
664 PURPLE_CBFLAGS_NONE, | |
665 TRUE); /* show a join message */ | |
666 | |
667 if (from != to) { | |
668 /* add them to our chat window */ | |
669 purple_debug_info("nullprpl", "%s sees that %s is in chat room %s\n", | |
670 from->nick, to->nick, room); | |
671 purple_conv_chat_add_user(from, | |
672 to->nick, | |
673 NULL, /* user-provided join message, IRC style */ | |
674 PURPLE_CBFLAGS_NONE, | |
675 FALSE); /* show a join message */ | |
676 } | |
677 } | |
678 | |
679 static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) { | |
680 char *username = gc->account->username; | |
681 char *room = g_hash_table_lookup(components, "room"); | |
682 int chat_id = g_str_hash(room); | |
683 purple_debug_info("nullprpl", "%s is joining chat room %s\n", username, room); | |
684 | |
685 if (!purple_find_chat(gc, chat_id)) { | |
686 serv_got_joined_chat(gc, chat_id, room); | |
687 | |
688 /* tell everyone that we joined, and add them if they're already there */ | |
689 foreach_gc_in_chat(joined_chat, gc, chat_id, NULL); | |
690 } else { | |
691 purple_debug_info("nullprpl", "%s is already in chat room %s\n", username, | |
692 room); | |
693 purple_notify_info(gc, | |
694 _("Join chat"), | |
695 _("Join chat"), | |
696 g_strdup_printf("%s is already in chat room %s.", | |
697 username, room)); | |
698 } | |
699 } | |
700 | |
701 static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) { | |
702 char *invited_by = g_hash_table_lookup(components, "invited_by"); | |
703 char *room = g_hash_table_lookup(components, "room"); | |
704 char *username = gc->account->username; | |
705 PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by); | |
706 char *message = g_strdup_printf( | |
707 "%s %s %s.", | |
708 username, | |
709 _("has rejected your invitation to join the chat room"), | |
710 room); | |
711 | |
712 purple_debug_info("nullprpl", | |
713 "%s has rejected %s's invitation to join chat room %s\n", | |
714 username, invited_by, room); | |
715 | |
716 purple_notify_info(invited_by_gc, | |
717 _("Chat invitation rejected"), | |
718 _("Chat invitation rejected"), | |
719 message); | |
720 } | |
721 | |
722 static char *nullprpl_get_chat_name(GHashTable *components) { | |
723 char *room = g_hash_table_lookup(components, "room"); | |
724 purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room); | |
725 return room; | |
726 } | |
727 | |
728 static void nullprpl_chat_invite(PurpleConnection *gc, int id, | |
729 const char *message, const char *who) { | |
730 char *username = gc->account->username; | |
731 PurpleConversation *conv = purple_find_chat(gc, id); | |
732 char *room = conv->name; | |
733 PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID); | |
734 | |
735 purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n", | |
736 username, who, room); | |
737 | |
738 if (to_acct) { | |
739 PurpleConversation *to_conv = purple_find_chat(to_acct->gc, id); | |
740 if (to_conv) { | |
741 purple_debug_info("nullprpl", | |
742 "%s is already in chat room %s; " | |
743 "ignoring invitation from %s\n", | |
744 who, room, username); | |
745 purple_notify_info(gc, | |
746 _("Chat invitation"), | |
747 _("Chat invitation"), | |
748 g_strdup_printf("%s is already in chat room %s.", | |
749 who, room)); | |
750 } else { | |
751 GHashTable *components; | |
752 components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free); | |
753 g_hash_table_replace(components, "room", g_strdup(room)); | |
754 g_hash_table_replace(components, "invited_by", g_strdup(username)); | |
755 serv_got_chat_invite(to_acct->gc, room, username, message, components); | |
756 } | |
757 } | |
758 } | |
759 | |
760 static void left_chat_room(PurpleConvChat *from, PurpleConvChat *to, | |
761 int id, const char *room, gpointer userdata) { | |
762 if (from != to) { | |
763 /* tell their chat window that we left */ | |
764 purple_debug_info("nullprpl", "%s sees that %s left chat room %s\n", | |
765 to->nick, from->nick, room); | |
766 purple_conv_chat_remove_user(to, | |
767 from->nick, | |
768 NULL); /* user-provided message, IRC style */ | |
769 } | |
770 } | |
771 | |
772 static void nullprpl_chat_leave(PurpleConnection *gc, int id) { | |
773 PurpleConversation *conv = purple_find_chat(gc, id); | |
774 purple_debug_info("nullprpl", "%s is leaving chat room %s\n", | |
775 gc->account->username, conv->name); | |
776 | |
777 /* tell everyone that we left */ | |
778 foreach_gc_in_chat(left_chat_room, gc, id, NULL); | |
779 } | |
780 | |
781 static PurpleCmdRet send_whisper(PurpleConversation *conv, const gchar *cmd, | |
782 gchar **args, gchar **error, void *userdata) { | |
783 const char *to_username; | |
784 const char *message; | |
785 const char *from_username; | |
786 PurpleConvChat *chat; | |
787 PurpleConvChatBuddy *chat_buddy; | |
788 PurpleConnection *to; | |
789 | |
790 /* parse args */ | |
791 to_username = args[0]; | |
792 message = args[1]; | |
793 | |
794 if (!to_username || strlen(to_username) == 0) { | |
795 *error = g_strdup(_("Whisper is missing recipient.")); | |
796 return PURPLE_CMD_RET_FAILED; | |
797 } else if (!message || strlen(message) == 0) { | |
798 *error = g_strdup(_("Whisper is missing message.")); | |
799 return PURPLE_CMD_RET_FAILED; | |
800 } | |
801 | |
802 from_username = conv->account->username; | |
803 purple_debug_info("nullprpl", "%s whispers to %s in chat room %s: %s\n", | |
804 from_username, to_username, conv->name, message); | |
805 | |
806 chat = purple_conversation_get_chat_data(conv); | |
807 chat_buddy = purple_conv_chat_cb_find(chat, to_username); | |
808 to = get_nullprpl_gc(to_username); | |
809 | |
810 if (!chat_buddy) { | |
811 /* this will be freed by the caller */ | |
812 *error = g_strdup_printf(_("%s is not logged in."), to_username); | |
813 return PURPLE_CMD_RET_FAILED; | |
814 } else if (!to) { | |
815 *error = g_strdup_printf(_("%s is not in this chat room."), to_username); | |
816 return PURPLE_CMD_RET_FAILED; | |
817 } else { | |
818 /* write the whisper in the sender's chat window */ | |
819 char *message_to = g_strdup_printf("%s (to %s)", message, to_username); | |
820 purple_conv_chat_write(chat, from_username, message_to, | |
821 PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_WHISPER, | |
822 time(NULL)); | |
823 g_free(message_to); | |
824 | |
825 /* send the whisper */ | |
826 serv_chat_whisper(to, chat->id, from_username, message); | |
827 | |
828 return PURPLE_CMD_RET_OK; | |
829 } | |
830 } | |
831 | |
832 static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who, | |
833 const char *message) { | |
834 char *username = gc->account->username; | |
835 PurpleConversation *conv = purple_find_chat(gc, id); | |
836 purple_debug_info("nullprpl", | |
837 "%s receives whisper from %s in chat room %s: %s\n", | |
838 username, who, conv->name, message); | |
839 | |
840 /* receive whisper on recipient's account */ | |
841 serv_got_chat_in(gc, id, who, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_WHISPER, | |
842 message, time(NULL)); | |
843 } | |
844 | |
845 static void receive_chat_message(PurpleConvChat *from, PurpleConvChat *to, | |
846 int id, const char *room, gpointer userdata) { | |
847 const char *message = (const char *)userdata; | |
848 PurpleConnection *to_gc = get_nullprpl_gc(to->nick); | |
849 | |
850 purple_debug_info("nullprpl", | |
851 "%s receives message from %s in chat room %s: %s\n", | |
852 to->nick, from->nick, room, message); | |
853 serv_got_chat_in(to_gc, id, from->nick, PURPLE_MESSAGE_RECV, message, | |
854 time(NULL)); | |
855 } | |
856 | |
857 static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message, | |
858 PurpleMessageFlags flags) { | |
859 char *username = gc->account->username; | |
860 PurpleConversation *conv = purple_find_chat(gc, id); | |
861 | |
862 if (conv) { | |
863 purple_debug_info("nullprpl", | |
864 "%s is sending message to chat room %s: %s\n", username, | |
865 conv->name, message); | |
866 | |
867 /* send message to everyone in the chat room */ | |
868 foreach_gc_in_chat(receive_chat_message, gc, id, (gpointer)message); | |
869 return 0; | |
870 } else { | |
871 purple_debug_info("nullprpl", | |
872 "tried to send message from %s to chat room #%d: %s\n" | |
873 "but couldn't find chat room", | |
874 username, id, message); | |
875 return -1; | |
876 } | |
877 } | |
878 | |
879 static void nullprpl_register_user(PurpleAccount *acct) { | |
880 purple_debug_info("nullprpl", "registering account for %s\n", | |
881 acct->username); | |
882 } | |
883 | |
884 static void nullprpl_get_cb_info(PurpleConnection *gc, int id, const char *who) { | |
885 PurpleConversation *conv = purple_find_chat(gc, id); | |
886 purple_debug_info("nullprpl", | |
887 "retrieving %s's info for %s in chat room %s\n", who, | |
888 gc->account->username, conv->name); | |
889 | |
890 nullprpl_get_info(gc, who); | |
891 } | |
892 | |
893 static void nullprpl_alias_buddy(PurpleConnection *gc, const char *who, | |
894 const char *alias) { | |
895 purple_debug_info("nullprpl", "%s sets %'s alias to %s\n", | |
896 gc->account->username, who, alias); | |
897 } | |
898 | |
899 static void nullprpl_group_buddy(PurpleConnection *gc, const char *who, | |
900 const char *old_group, | |
901 const char *new_group) { | |
902 purple_debug_info("nullprpl", "%s has moved %s from group %s to group %s\n", | |
903 who, old_group, new_group); | |
904 } | |
905 | |
906 static void nullprpl_rename_group(PurpleConnection *gc, const char *old_name, | |
907 PurpleGroup *group, GList *moved_buddies) { | |
908 purple_debug_info("nullprpl", "%s has renamed group %s to %s\n", | |
909 gc->account->username, old_name, group->name); | |
910 } | |
911 | |
912 static void nullprpl_convo_closed(PurpleConnection *gc, const char *who) { | |
913 purple_debug_info("nullprpl", "%s's conversation with %s was closed\n", | |
914 gc->account->username, who); | |
915 } | |
916 | |
917 /* normalize a username (e.g. remove whitespace, add default domain, etc.) | |
918 * for nullprpl, this is a noop. | |
919 */ | |
920 static const char *nullprpl_normalize(const PurpleAccount *acct, | |
921 const char *input) { | |
922 return NULL; | |
923 } | |
924 | |
925 static void nullprpl_set_buddy_icon(PurpleConnection *gc, | |
926 PurpleStoredImage *img) { | |
927 purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n", | |
928 gc->account->username, purple_imgstore_get_filename(img)); | |
929 } | |
930 | |
931 static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) { | |
932 purple_debug_info("nullprpl", "%s has removed group %s\n", | |
933 gc->account->username, group->name); | |
934 } | |
935 | |
936 | |
937 static void set_chat_topic_fn(PurpleConvChat *from, PurpleConvChat *to, | |
938 int id, const char *room, gpointer userdata) { | |
939 const char *topic = (const char *)userdata; | |
940 const char *username = from->conv->account->username; | |
941 char *msg; | |
942 | |
943 purple_conv_chat_set_topic(to, username, topic); | |
944 | |
945 if (topic && strlen(topic) > 0) | |
946 msg = g_strdup_printf(_("%s sets topic to: %s"), username, topic); | |
947 else | |
948 msg = g_strdup_printf(_("%s clears topic"), username); | |
949 | |
950 purple_conv_chat_write(to, username, msg, | |
951 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, | |
952 time(NULL)); | |
953 g_free(msg); | |
954 } | |
955 | |
956 static void nullprpl_set_chat_topic(PurpleConnection *gc, int id, | |
957 const char *topic) { | |
958 PurpleConversation *conv = purple_find_chat(gc, id); | |
959 PurpleConvChat *chat = purple_conversation_get_chat_data(conv); | |
960 const char *last_topic; | |
961 | |
962 if (!chat) | |
963 return; | |
964 | |
965 purple_debug_info("nullprpl", "%s sets topic of chat room '%s' to '%s'\n", | |
966 gc->account->username, conv->name, topic); | |
967 | |
968 last_topic = purple_conv_chat_get_topic(chat); | |
969 if ((!topic && !last_topic) || | |
970 (topic && last_topic && !strcmp(topic, last_topic))) | |
971 return; /* topic is unchanged, this is a noop */ | |
972 | |
973 foreach_gc_in_chat(set_chat_topic_fn, gc, id, (gpointer)topic); | |
974 } | |
975 | |
976 static gboolean nullprpl_finish_get_roomlist(gpointer roomlist) { | |
977 purple_roomlist_set_in_progress((PurpleRoomlist *)roomlist, FALSE); | |
978 return FALSE; | |
979 } | |
980 | |
981 static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) { | |
982 char *username = gc->account->username; | |
983 PurpleRoomlist *roomlist = purple_roomlist_new(gc->account); | |
984 GList *fields = NULL; | |
985 PurpleRoomlistField *field; | |
986 GList *chats; | |
987 GList *seen_ids = NULL; | |
988 | |
989 purple_debug_info("nullprpl", "%s asks for room list; returning:\n", username); | |
990 | |
991 /* set up the room list */ | |
992 field = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "room", | |
993 "room", TRUE /* hidden */); | |
994 fields = g_list_append(fields, field); | |
995 | |
996 field = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, "Id", "Id", FALSE); | |
997 fields = g_list_append(fields, field); | |
998 | |
999 purple_roomlist_set_fields(roomlist, fields); | |
1000 | |
1001 /* add each chat room. the chat ids are cached in seen_ids so that each room | |
1002 * is only returned once, even if multiple users are in it. */ | |
1003 for (chats = purple_get_chats(); chats; chats = g_list_next(chats)) { | |
1004 PurpleConversation *conv = (PurpleConversation *)chats->data; | |
1005 PurpleRoomlistRoom *room; | |
1006 char *name = conv->name; | |
1007 int id = purple_conversation_get_chat_data(conv)->id; | |
1008 | |
1009 /* have we already added this room? */ | |
1010 if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp)) | |
1011 continue; /* yes! try the next one. */ | |
1012 | |
1013 seen_ids = g_list_append(seen_ids, name); /* no, it's new. */ | |
1014 purple_debug_info("nullprpl", "%s (%d), ", name, id); | |
1015 | |
1016 room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); | |
1017 purple_roomlist_room_add_field(roomlist, room, name); | |
1018 purple_roomlist_room_add_field(roomlist, room, &id); | |
1019 purple_roomlist_room_add(roomlist, room); | |
1020 } | |
1021 | |
1022 purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, roomlist); | |
1023 return roomlist; | |
1024 } | |
1025 | |
1026 static void nullprpl_roomlist_cancel(PurpleRoomlist *list) { | |
1027 purple_debug_info("nullprpl", "%s asked to cancel room list request\n", | |
1028 list->account->username); | |
1029 } | |
1030 | |
1031 static void nullprpl_roomlist_expand_category(PurpleRoomlist *list, | |
1032 PurpleRoomlistRoom *category) { | |
1033 purple_debug_info("nullprpl", "%s asked to expand room list category %s\n", | |
1034 list->account->username, category->name); | |
1035 } | |
1036 | |
1037 /* nullprpl doesn't support file transfer...yet... */ | |
1038 static gboolean nullprpl_can_receive_file(PurpleConnection *gc, | |
1039 const char *who) { | |
1040 return FALSE; | |
1041 } | |
1042 | |
1043 static gboolean nullprpl_offline_message(const PurpleBuddy *buddy) { | |
1044 purple_debug_info("nullprpl", | |
1045 "reporting that offline messages are supported for %s\n", | |
1046 buddy->name); | |
1047 return TRUE; | |
1048 } | |
1049 | |
1050 | |
1051 /* | |
1052 * prpl stuff. see prpl.h for more information. | |
1053 */ | |
1054 | |
1055 static PurplePluginProtocolInfo prpl_info = | |
1056 { | |
1057 OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC, /* options */ | |
1058 NULL, /* user_splits, initialized in nullprpl_init() */ | |
1059 NULL, /* protocol_options, initialized in nullprpl_init() */ | |
1060 { /* icon_spec, a PurpleBuddyIconSpec */ | |
1061 "png,jpg,gif", /* format */ | |
1062 0, /* min_width */ | |
1063 0, /* min_height */ | |
1064 128, /* max_width */ | |
1065 128, /* max_height */ | |
1066 10000, /* max_filesize */ | |
1067 PURPLE_ICON_SCALE_DISPLAY, /* scale_rules */ | |
1068 }, | |
1069 nullprpl_list_icon, /* list_icon */ | |
1070 nullprpl_list_emblem, /* list_emblem */ | |
1071 nullprpl_status_text, /* status_text */ | |
1072 nullprpl_tooltip_text, /* tooltip_text */ | |
1073 nullprpl_status_types, /* status_types */ | |
1074 nullprpl_blist_node_menu, /* blist_node_menu */ | |
1075 nullprpl_chat_info, /* chat_info */ | |
1076 nullprpl_chat_info_defaults, /* chat_info_defaults */ | |
1077 nullprpl_login, /* login */ | |
1078 nullprpl_close, /* close */ | |
1079 nullprpl_send_im, /* send_im */ | |
1080 nullprpl_set_info, /* set_info */ | |
1081 nullprpl_send_typing, /* send_typing */ | |
1082 nullprpl_get_info, /* get_info */ | |
1083 nullprpl_set_status, /* set_status */ | |
1084 nullprpl_set_idle, /* set_idle */ | |
1085 nullprpl_change_passwd, /* change_passwd */ | |
1086 nullprpl_add_buddy, /* add_buddy */ | |
1087 nullprpl_add_buddies, /* add_buddies */ | |
1088 nullprpl_remove_buddy, /* remove_buddy */ | |
1089 nullprpl_remove_buddies, /* remove_buddies */ | |
1090 nullprpl_add_permit, /* add_permit */ | |
1091 nullprpl_add_deny, /* add_deny */ | |
1092 nullprpl_rem_permit, /* rem_permit */ | |
1093 nullprpl_rem_deny, /* rem_deny */ | |
1094 nullprpl_set_permit_deny, /* set_permit_deny */ | |
1095 nullprpl_join_chat, /* join_chat */ | |
1096 nullprpl_reject_chat, /* reject_chat */ | |
1097 nullprpl_get_chat_name, /* get_chat_name */ | |
1098 nullprpl_chat_invite, /* chat_invite */ | |
1099 nullprpl_chat_leave, /* chat_leave */ | |
1100 nullprpl_chat_whisper, /* chat_whisper */ | |
1101 nullprpl_chat_send, /* chat_send */ | |
1102 NULL, /* keepalive */ | |
1103 nullprpl_register_user, /* register_user */ | |
1104 nullprpl_get_cb_info, /* get_cb_info */ | |
1105 NULL, /* get_cb_away */ | |
1106 nullprpl_alias_buddy, /* alias_buddy */ | |
1107 nullprpl_group_buddy, /* group_buddy */ | |
1108 nullprpl_rename_group, /* rename_group */ | |
1109 NULL, /* buddy_free */ | |
1110 nullprpl_convo_closed, /* convo_closed */ | |
1111 nullprpl_normalize, /* normalize */ | |
1112 nullprpl_set_buddy_icon, /* set_buddy_icon */ | |
1113 nullprpl_remove_group, /* remove_group */ | |
1114 NULL, /* get_cb_real_name */ | |
1115 nullprpl_set_chat_topic, /* set_chat_topic */ | |
1116 NULL, /* find_blist_chat */ | |
1117 nullprpl_roomlist_get_list, /* roomlist_get_list */ | |
1118 nullprpl_roomlist_cancel, /* roomlist_cancel */ | |
1119 nullprpl_roomlist_expand_category, /* roomlist_expand_category */ | |
1120 nullprpl_can_receive_file, /* can_receive_file */ | |
1121 NULL, /* send_file */ | |
1122 NULL, /* new_xfer */ | |
1123 nullprpl_offline_message, /* offline_message */ | |
1124 NULL, /* whiteboard_prpl_ops */ | |
1125 NULL, /* send_raw */ | |
1126 NULL, /* roomlist_room_serialize */ | |
1127 NULL, /* padding... */ | |
1128 NULL, | |
1129 NULL, | |
1130 NULL, | |
1131 }; | |
1132 | |
1133 static void nullprpl_init(PurplePlugin *plugin) | |
1134 { | |
1135 /* see accountopt.h for information about user splits and protocol options */ | |
1136 PurpleAccountUserSplit *split = purple_account_user_split_new( | |
1137 _("Example user split (unused)"), /* text shown to user */ | |
1138 "default", /* default value */ | |
1139 '@'); /* field separator */ | |
1140 PurpleAccountOption *option = purple_account_option_string_new( | |
1141 _("Example option (unused)"), /* text shown to user */ | |
1142 "example", /* pref name */ | |
1143 "default"); /* default value */ | |
1144 | |
1145 purple_debug_info("nullprpl", "starting up\n"); | |
1146 | |
1147 prpl_info.user_splits = g_list_append(NULL, split); | |
1148 prpl_info.protocol_options = g_list_append(NULL, option); | |
1149 | |
1150 /* register whisper chat command, /msg */ | |
1151 purple_cmd_register("msg", | |
1152 "ws", /* args: recipient and message */ | |
1153 PURPLE_CMD_P_DEFAULT, /* priority */ | |
1154 PURPLE_CMD_FLAG_CHAT, | |
1155 "prpl-null", | |
1156 send_whisper, | |
1157 "msg <username> <message>: send a private message, aka a whisper", | |
1158 NULL); /* userdata */ | |
1159 | |
1160 /* get ready to store offline messages */ | |
1161 goffline_messages = g_hash_table_new_full(g_str_hash, /* hash fn */ | |
1162 g_str_equal, /* key comparison fn */ | |
1163 g_free, /* key free fn */ | |
1164 NULL); /* value free fn */ | |
1165 | |
1166 _null_protocol = plugin; | |
1167 } | |
1168 | |
1169 static void nullprpl_destroy(PurplePlugin *plugin) { | |
1170 purple_debug_info("nullprpl", "shutting down\n"); | |
1171 } | |
1172 | |
1173 | |
1174 static PurplePluginInfo info = | |
1175 { | |
1176 PURPLE_PLUGIN_MAGIC, /* magic */ | |
1177 PURPLE_MAJOR_VERSION, /* major_version */ | |
1178 PURPLE_MINOR_VERSION, /* minor_version */ | |
1179 PURPLE_PLUGIN_PROTOCOL, /* type */ | |
1180 NULL, /* ui_requirement */ | |
1181 0, /* flags */ | |
1182 NULL, /* dependencies */ | |
1183 PURPLE_PRIORITY_DEFAULT, /* priority */ | |
1184 NULLPRPL_ID, /* id */ | |
1185 "Nullprpl", /* name */ | |
1186 "0.3", /* version */ | |
1187 "Null Protocol Plugin", /* summary */ | |
1188 "Null Protocol Plugin", /* description */ | |
1189 "Ryan Barrett <nullprpl@ryanb.org>", /* author */ | |
1190 "http://snarfed.org/space/pidgin+null+protocol+plugin", /* homepage */ | |
1191 NULL, /* load */ | |
1192 NULL, /* unload */ | |
1193 nullprpl_destroy, /* destroy */ | |
1194 NULL, /* ui_info */ | |
1195 &prpl_info, /* extra_info */ | |
1196 NULL, /* prefs_info */ | |
1197 nullprpl_actions, /* actions */ | |
1198 NULL, /* padding... */ | |
1199 NULL, | |
1200 NULL, | |
1201 NULL, | |
1202 }; | |
1203 | |
1204 PURPLE_INIT_PLUGIN(null, nullprpl_init, info); |