applied changes from fb4435d514398a0b1febebe8bf46339e2c2b52b6
[pidgin-git.git] / libpurple / prpl.c
blob7849189d773e250fb49cc24fac8ba6676017ec07
1 /*
2 * purple
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.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
24 #include "conversation.h"
25 #include "debug.h"
26 #include "network.h"
27 #include "notify.h"
28 #include "prpl.h"
29 #include "request.h"
30 #include "util.h"
32 /**************************************************************************/
33 /** @name Attention Type API */
34 /**************************************************************************/
35 PurpleAttentionType *
36 purple_attention_type_new(const char *ulname, const char *name,
37 const char *inc_desc, const char *out_desc)
39 PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1);
41 purple_attention_type_set_name(attn, name);
42 purple_attention_type_set_incoming_desc(attn, inc_desc);
43 purple_attention_type_set_outgoing_desc(attn, out_desc);
44 purple_attention_type_set_unlocalized_name(attn, ulname);
46 return attn;
50 void
51 purple_attention_type_set_name(PurpleAttentionType *type, const char *name)
53 g_return_if_fail(type != NULL);
55 type->name = name;
58 void
59 purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc)
61 g_return_if_fail(type != NULL);
63 type->incoming_description = desc;
66 void
67 purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc)
69 g_return_if_fail(type != NULL);
71 type->outgoing_description = desc;
74 void
75 purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name)
77 g_return_if_fail(type != NULL);
79 type->icon_name = name;
82 void
83 purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname)
85 g_return_if_fail(type != NULL);
87 type->unlocalized_name = ulname;
90 const char *
91 purple_attention_type_get_name(const PurpleAttentionType *type)
93 g_return_val_if_fail(type != NULL, NULL);
95 return type->name;
98 const char *
99 purple_attention_type_get_incoming_desc(const PurpleAttentionType *type)
101 g_return_val_if_fail(type != NULL, NULL);
103 return type->incoming_description;
106 const char *
107 purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type)
109 g_return_val_if_fail(type != NULL, NULL);
111 return type->outgoing_description;
114 const char *
115 purple_attention_type_get_icon_name(const PurpleAttentionType *type)
117 g_return_val_if_fail(type != NULL, NULL);
119 if(type->icon_name == NULL || *(type->icon_name) == '\0')
120 return NULL;
122 return type->icon_name;
125 const char *
126 purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type)
128 g_return_val_if_fail(type != NULL, NULL);
130 return type->unlocalized_name;
133 /**************************************************************************/
134 /** @name Protocol Plugin API */
135 /**************************************************************************/
136 void
137 purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle,
138 time_t idle_time)
140 g_return_if_fail(account != NULL);
141 g_return_if_fail(purple_account_is_connected(account));
143 purple_presence_set_idle(purple_account_get_presence(account),
144 idle, idle_time);
147 void
148 purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time)
150 PurplePresence *presence;
152 g_return_if_fail(account != NULL);
153 g_return_if_fail(purple_account_is_connected(account));
155 if (login_time == 0)
156 login_time = time(NULL);
158 presence = purple_account_get_presence(account);
160 purple_presence_set_login_time(presence, login_time);
163 void
164 purple_prpl_got_account_status(PurpleAccount *account, const char *status_id, ...)
166 PurplePresence *presence;
167 PurpleStatus *status;
168 va_list args;
170 g_return_if_fail(account != NULL);
171 g_return_if_fail(status_id != NULL);
172 g_return_if_fail(purple_account_is_connected(account));
174 presence = purple_account_get_presence(account);
175 status = purple_presence_get_status(presence, status_id);
177 g_return_if_fail(status != NULL);
179 va_start(args, status_id);
180 purple_status_set_active_with_attrs(status, TRUE, args);
181 va_end(args);
184 void
185 purple_prpl_got_account_actions(PurpleAccount *account)
188 g_return_if_fail(account != NULL);
189 g_return_if_fail(purple_account_is_connected(account));
191 purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed",
192 account);
195 void
196 purple_prpl_got_user_idle(PurpleAccount *account, const char *name,
197 gboolean idle, time_t idle_time)
199 PurplePresence *presence;
200 GSList *list;
202 g_return_if_fail(account != NULL);
203 g_return_if_fail(name != NULL);
204 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
206 if ((list = purple_find_buddies(account, name)) == NULL)
207 return;
209 while (list) {
210 presence = purple_buddy_get_presence(list->data);
211 list = g_slist_delete_link(list, list);
212 purple_presence_set_idle(presence, idle, idle_time);
216 void
217 purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
218 time_t login_time)
220 GSList *list;
221 PurplePresence *presence;
223 g_return_if_fail(account != NULL);
224 g_return_if_fail(name != NULL);
226 if ((list = purple_find_buddies(account, name)) == NULL)
227 return;
229 if (login_time == 0)
230 login_time = time(NULL);
232 while (list) {
233 PurpleBuddy *buddy = list->data;
234 presence = purple_buddy_get_presence(buddy);
235 list = g_slist_delete_link(list, list);
237 if (purple_presence_get_login_time(presence) != login_time)
239 purple_presence_set_login_time(presence, login_time);
241 purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy);
246 void
247 purple_prpl_got_user_status(PurpleAccount *account, const char *name,
248 const char *status_id, ...)
250 GSList *list, *l;
251 PurpleBuddy *buddy;
252 PurplePresence *presence;
253 PurpleStatus *status;
254 PurpleStatus *old_status;
255 va_list args;
257 g_return_if_fail(account != NULL);
258 g_return_if_fail(name != NULL);
259 g_return_if_fail(status_id != NULL);
260 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
262 if((list = purple_find_buddies(account, name)) == NULL)
263 return;
265 for(l = list; l != NULL; l = l->next) {
266 buddy = l->data;
268 presence = purple_buddy_get_presence(buddy);
269 status = purple_presence_get_status(presence, status_id);
271 if(NULL == status)
273 * TODO: This should never happen, right? We should call
274 * g_warning() or something.
276 continue;
278 old_status = purple_presence_get_active_status(presence);
280 va_start(args, status_id);
281 purple_status_set_active_with_attrs(status, TRUE, args);
282 va_end(args);
284 purple_blist_update_buddy_status(buddy, old_status);
287 g_slist_free(list);
289 /* The buddy is no longer online, they are therefore by definition not
290 * still typing to us. */
291 if (!purple_status_is_online(status)) {
292 serv_got_typing_stopped(purple_account_get_connection(account), name);
293 purple_prpl_got_media_caps(account, name);
297 void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
298 const char *status_id)
300 GSList *list, *l;
301 PurpleBuddy *buddy;
302 PurplePresence *presence;
303 PurpleStatus *status;
305 g_return_if_fail(account != NULL);
306 g_return_if_fail(name != NULL);
307 g_return_if_fail(status_id != NULL);
308 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
310 if((list = purple_find_buddies(account, name)) == NULL)
311 return;
313 for(l = list; l != NULL; l = l->next) {
314 buddy = l->data;
316 presence = purple_buddy_get_presence(buddy);
317 status = purple_presence_get_status(presence, status_id);
319 if(NULL == status)
320 continue;
322 if (purple_status_is_active(status)) {
323 purple_status_set_active(status, FALSE);
324 purple_blist_update_buddy_status(buddy, status);
328 g_slist_free(list);
331 static void
332 do_prpl_change_account_status(PurpleAccount *account,
333 PurpleStatus *old_status, PurpleStatus *new_status)
335 PurplePlugin *prpl;
336 PurplePluginProtocolInfo *prpl_info;
338 if (purple_status_is_online(new_status) &&
339 purple_account_is_disconnected(account) &&
340 purple_network_is_available())
342 purple_account_connect(account);
343 return;
346 if (!purple_status_is_online(new_status))
348 if (!purple_account_is_disconnected(account))
349 purple_account_disconnect(account);
350 /* Clear out the unsaved password if we're already disconnected and we switch to offline status */
351 else if (!purple_account_get_remember_password(account))
352 purple_account_set_password(account, NULL);
353 return;
356 if (purple_account_is_connecting(account))
358 * We don't need to call the set_status PRPL function because
359 * the PRPL will take care of setting its status during the
360 * connection process.
362 return;
364 prpl = purple_find_prpl(purple_account_get_protocol_id(account));
366 if (prpl == NULL)
367 return;
369 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
371 if (!purple_account_is_disconnected(account) && prpl_info->set_status != NULL)
373 prpl_info->set_status(account, new_status);
377 void
378 purple_prpl_change_account_status(PurpleAccount *account,
379 PurpleStatus *old_status, PurpleStatus *new_status)
381 g_return_if_fail(account != NULL);
382 g_return_if_fail(new_status != NULL);
383 g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL);
385 do_prpl_change_account_status(account, old_status, new_status);
387 purple_signal_emit(purple_accounts_get_handle(), "account-status-changed",
388 account, old_status, new_status);
391 GList *
392 purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence)
394 GList *statuses = NULL;
395 GList *l;
396 PurpleStatus *status;
398 g_return_val_if_fail(account != NULL, NULL);
399 g_return_val_if_fail(presence != NULL, NULL);
401 for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
403 status = purple_status_new((PurpleStatusType *)l->data, presence);
404 statuses = g_list_prepend(statuses, status);
407 statuses = g_list_reverse(statuses);
409 return statuses;
412 static void
413 purple_prpl_attention(PurpleConversation *conv, const char *who,
414 guint type, PurpleMessageFlags flags, time_t mtime)
416 PurpleAccount *account = purple_conversation_get_account(conv);
417 purple_signal_emit(purple_conversations_get_handle(),
418 flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention",
419 account, who, conv, type);
422 void
423 purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code)
425 PurpleAttentionType *attn;
426 PurpleMessageFlags flags;
427 PurplePlugin *prpl;
428 PurpleConversation *conv;
429 gboolean (*send_attention)(PurpleConnection *, const char *, guint);
430 PurpleBuddy *buddy;
431 const char *alias;
432 gchar *description;
433 time_t mtime;
435 g_return_if_fail(gc != NULL);
436 g_return_if_fail(who != NULL);
438 prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account));
439 send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention;
440 g_return_if_fail(send_attention != NULL);
442 mtime = time(NULL);
444 attn = purple_get_attention_type_from_code(gc->account, type_code);
446 if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
447 alias = purple_buddy_get_contact_alias(buddy);
448 else
449 alias = who;
451 if (attn && purple_attention_type_get_outgoing_desc(attn)) {
452 description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
453 } else {
454 description = g_strdup_printf(_("Requesting %s's attention..."), alias);
457 flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
459 purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
460 description, who);
462 if (!send_attention(gc, who, type_code))
463 return;
465 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
466 purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
467 purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
469 g_free(description);
472 static void
473 got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
475 PurpleMessageFlags flags;
476 PurpleAttentionType *attn;
477 PurpleBuddy *buddy;
478 const char *alias;
479 gchar *description;
480 time_t mtime;
482 mtime = time(NULL);
484 attn = purple_get_attention_type_from_code(gc->account, type_code);
486 /* PURPLE_MESSAGE_NOTIFY is for attention messages. */
487 flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
489 /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
490 * it next to the attention command. And if it is null, display a generic icon. */
492 if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
493 alias = purple_buddy_get_contact_alias(buddy);
494 else
495 alias = who;
497 if (attn && purple_attention_type_get_incoming_desc(attn)) {
498 description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias);
499 } else {
500 description = g_strdup_printf(_("%s has requested your attention!"), alias);
503 purple_debug_info("server", "got_attention: got '%s' from %s\n",
504 description, who);
506 if (id == -1)
507 serv_got_im(gc, who, description, flags, mtime);
508 else
509 serv_got_chat_in(gc, id, who, flags, description, mtime);
511 /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
513 g_free(description);
516 void
517 purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code)
519 PurpleConversation *conv = NULL;
520 PurpleAccount *account = purple_connection_get_account(gc);
522 got_attention(gc, -1, who, type_code);
523 conv =
524 purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
525 if (conv)
526 purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
527 time(NULL));
530 void
531 purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code)
533 got_attention(gc, id, who, type_code);
536 gboolean
537 purple_prpl_initiate_media(PurpleAccount *account,
538 const char *who,
539 PurpleMediaSessionType type)
541 #ifdef USE_VV
542 PurpleConnection *gc = NULL;
543 PurplePlugin *prpl = NULL;
544 PurplePluginProtocolInfo *prpl_info = NULL;
546 if (account)
547 gc = purple_account_get_connection(account);
548 if (gc)
549 prpl = purple_connection_get_prpl(gc);
550 if (prpl)
551 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
553 if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, initiate_media)) {
554 /* should check that the protocol supports this media type here? */
555 return prpl_info->initiate_media(account, who, type);
556 } else
557 #endif
558 return FALSE;
561 PurpleMediaCaps
562 purple_prpl_get_media_caps(PurpleAccount *account, const char *who)
564 #ifdef USE_VV
565 PurpleConnection *gc = NULL;
566 PurplePlugin *prpl = NULL;
567 PurplePluginProtocolInfo *prpl_info = NULL;
569 if (account)
570 gc = purple_account_get_connection(account);
571 if (gc)
572 prpl = purple_connection_get_prpl(gc);
573 if (prpl)
574 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
576 if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info,
577 get_media_caps)) {
578 return prpl_info->get_media_caps(account, who);
580 #endif
581 return PURPLE_MEDIA_CAPS_NONE;
584 void
585 purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
587 #ifdef USE_VV
588 GSList *list;
590 g_return_if_fail(account != NULL);
591 g_return_if_fail(name != NULL);
593 if ((list = purple_find_buddies(account, name)) == NULL)
594 return;
596 while (list) {
597 PurpleBuddy *buddy = list->data;
598 PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy);
599 PurpleMediaCaps newcaps = 0;
600 const gchar *bname = purple_buddy_get_name(buddy);
601 list = g_slist_delete_link(list, list);
604 newcaps = purple_prpl_get_media_caps(account, bname);
605 purple_buddy_set_media_caps(buddy, newcaps);
607 if (oldcaps == newcaps)
608 continue;
610 purple_signal_emit(purple_blist_get_handle(),
611 "buddy-caps-changed", buddy,
612 newcaps, oldcaps);
614 #endif
617 /**************************************************************************
618 * Protocol Plugin Subsystem API
619 **************************************************************************/
621 PurplePlugin *
622 purple_find_prpl(const char *id)
624 GList *l;
625 PurplePlugin *plugin;
627 g_return_val_if_fail(id != NULL, NULL);
629 for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
630 plugin = (PurplePlugin *)l->data;
632 if (purple_strequal(plugin->info->id, id))
633 return plugin;
636 return NULL;