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  * 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.
12  *
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.
17  *
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
21  *
22  */
23 
24 /* This file is the fullcrap */
25 
26 #include "internal.h"
27 #include "blist.h"
28 #include "conversation.h"
29 #include "debug.h"
30 #include "log.h"
31 #include "notify.h"
32 #include "prefs.h"
33 #include "privacy.h"
34 #include "prpl.h"
35 #include "request.h"
36 #include "signals.h"
37 #include "server.h"
38 #include "status.h"
39 #include "util.h"
40 
41 #define SECS_BEFORE_RESENDING_AUTORESPONSE 600
42 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
43 
44 unsigned int
serv_send_typing(PurpleConnection * gc,const char * name,PurpleTypingState state)45 serv_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
46 {
47 	PurplePlugin *prpl;
48 	PurplePluginProtocolInfo *prpl_info;
49 
50 	if (gc) {
51 		prpl = purple_connection_get_prpl(gc);
52 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
53 
54 		if (prpl_info->send_typing)
55 			return prpl_info->send_typing(gc, name, state);
56 	}
57 
58 	return 0;
59 }
60 
61 static GSList *last_auto_responses = NULL;
62 struct last_auto_response {
63 	PurpleConnection *gc;
64 	char name[80];
65 	time_t sent;
66 };
67 
68 static gboolean
expire_last_auto_responses(gpointer data)69 expire_last_auto_responses(gpointer data)
70 {
71 	GSList *tmp, *cur;
72 	struct last_auto_response *lar;
73 
74 	tmp = last_auto_responses;
75 
76 	while (tmp) {
77 		cur = tmp;
78 		tmp = tmp->next;
79 		lar = (struct last_auto_response *)cur->data;
80 
81 		if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) {
82 			last_auto_responses = g_slist_remove(last_auto_responses, lar);
83 			g_free(lar);
84 		}
85 	}
86 
87 	return FALSE; /* do not run again */
88 }
89 
90 static struct last_auto_response *
get_last_auto_response(PurpleConnection * gc,const char * name)91 get_last_auto_response(PurpleConnection *gc, const char *name)
92 {
93 	GSList *tmp;
94 	struct last_auto_response *lar;
95 
96 	/* because we're modifying or creating a lar, schedule the
97 	 * function to expire them as the pref dictates */
98 	purple_timeout_add_seconds((SECS_BEFORE_RESENDING_AUTORESPONSE + 1), expire_last_auto_responses, NULL);
99 
100 	tmp = last_auto_responses;
101 
102 	while (tmp) {
103 		lar = (struct last_auto_response *)tmp->data;
104 
105 		if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name)))
106 			return lar;
107 
108 		tmp = tmp->next;
109 	}
110 
111 	lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1);
112 	g_snprintf(lar->name, sizeof(lar->name), "%s", name);
113 	lar->gc = gc;
114 	lar->sent = 0;
115 	last_auto_responses = g_slist_prepend(last_auto_responses, lar);
116 
117 	return lar;
118 }
119 
serv_send_im(PurpleConnection * gc,const char * name,const char * message,PurpleMessageFlags flags)120 int serv_send_im(PurpleConnection *gc, const char *name, const char *message,
121 				 PurpleMessageFlags flags)
122 {
123 	PurpleConversation *conv = NULL;
124 	PurpleAccount *account = NULL;
125 	PurplePresence *presence = NULL;
126 	PurplePlugin *prpl = NULL;
127 	PurplePluginProtocolInfo *prpl_info = NULL;
128 	int val = -EINVAL;
129 	const gchar *auto_reply_pref = NULL;
130 
131 	g_return_val_if_fail(gc != NULL, val);
132 
133 	prpl = purple_connection_get_prpl(gc);
134 
135 	g_return_val_if_fail(prpl != NULL, val);
136 
137 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
138 
139 	account  = purple_connection_get_account(gc);
140 	presence = purple_account_get_presence(account);
141 
142 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
143 
144 	if (prpl_info->send_im)
145 		val = prpl_info->send_im(gc, name, message, flags);
146 
147 	/*
148 	 * XXX - If "only auto-reply when away & idle" is set, then shouldn't
149 	 * this only reset lar->sent if we're away AND idle?
150 	 */
151 	auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
152 	if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
153 			!purple_presence_is_available(presence) &&
154 			!purple_strequal(auto_reply_pref, "never")) {
155 
156 		struct last_auto_response *lar;
157 		lar = get_last_auto_response(gc, name);
158 		lar->sent = time(NULL);
159 	}
160 
161 	if(conv && purple_conv_im_get_send_typed_timeout(PURPLE_CONV_IM(conv)))
162 		purple_conv_im_stop_send_typed_timeout(PURPLE_CONV_IM(conv));
163 
164 	return val;
165 }
166 
serv_get_info(PurpleConnection * gc,const char * name)167 void serv_get_info(PurpleConnection *gc, const char *name)
168 {
169 	PurplePlugin *prpl;
170 	PurplePluginProtocolInfo *prpl_info;
171 
172 	if (gc) {
173 		prpl = purple_connection_get_prpl(gc);
174 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
175 
176 		if (prpl_info->get_info)
177 			prpl_info->get_info(gc, name);
178 	}
179 }
180 
serv_set_info(PurpleConnection * gc,const char * info)181 void serv_set_info(PurpleConnection *gc, const char *info)
182 {
183 	PurplePlugin *prpl;
184 	PurplePluginProtocolInfo *prpl_info;
185 	PurpleAccount *account;
186 
187 	if (gc) {
188 		prpl = purple_connection_get_prpl(gc);
189 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
190 
191 		if (prpl_info->set_info) {
192 			account = purple_connection_get_account(gc);
193 
194 			if (purple_signal_emit_return_1(purple_accounts_get_handle(),
195 					"account-setting-info", account, info))
196 				return;
197 
198 			prpl_info->set_info(gc, info);
199 
200 			purple_signal_emit(purple_accounts_get_handle(),
201 					"account-set-info", account, info);
202 		}
203 	}
204 }
205 
206 /*
207  * Set buddy's alias on server roster/list
208  */
serv_alias_buddy(PurpleBuddy * b)209 void serv_alias_buddy(PurpleBuddy *b)
210 {
211 	PurpleAccount *account;
212 	PurpleConnection *gc;
213 	PurplePlugin *prpl;
214 	PurplePluginProtocolInfo *prpl_info;
215 
216 	if (b) {
217 		account = purple_buddy_get_account(b);
218 
219 		if (account) {
220 			gc = purple_account_get_connection(account);
221 
222 			if (gc) {
223 				prpl = purple_connection_get_prpl(gc);
224 				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
225 
226 				if (prpl_info->alias_buddy)
227 					prpl_info->alias_buddy(gc,
228 							purple_buddy_get_name(b),
229 							purple_buddy_get_local_buddy_alias(b));
230 			}
231 		}
232 	}
233 }
234 
235 void
serv_got_alias(PurpleConnection * gc,const char * who,const char * alias)236 serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
237 {
238 	PurpleAccount *account;
239 	GSList *buddies;
240 	PurpleBuddy *b;
241 	PurpleConversation *conv;
242 
243 	account = purple_connection_get_account(gc);
244 	buddies = purple_find_buddies(account, who);
245 
246 	while (buddies != NULL)
247 	{
248 		const char *server_alias;
249 
250 		b = buddies->data;
251 		buddies = g_slist_delete_link(buddies, buddies);
252 
253 		server_alias = purple_buddy_get_server_alias(b);
254 
255 		if (purple_strequal(server_alias, alias))
256 			continue;
257 
258 		purple_blist_server_alias_buddy(b, alias);
259 
260 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(b), account);
261 		if (conv != NULL && alias != NULL && !purple_strequal(alias, who))
262 		{
263 			char *escaped = g_markup_escape_text(who, -1);
264 			char *escaped2 = g_markup_escape_text(alias, -1);
265 			char *tmp = g_strdup_printf(_("%s is now known as %s.\n"),
266 										escaped, escaped2);
267 
268 			purple_conversation_write(conv, NULL, tmp,
269 					PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
270 					time(NULL));
271 
272 			g_free(tmp);
273 			g_free(escaped2);
274 			g_free(escaped);
275 		}
276 	}
277 }
278 
279 void
purple_serv_got_private_alias(PurpleConnection * gc,const char * who,const char * alias)280 purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char *alias)
281 {
282 	PurpleAccount *account = NULL;
283 	GSList *buddies = NULL;
284 	PurpleBuddy *b = NULL;
285 
286 	account = purple_connection_get_account(gc);
287 	buddies = purple_find_buddies(account, who);
288 
289 	while(buddies != NULL) {
290 		const char *balias;
291 		b = buddies->data;
292 
293 		buddies = g_slist_delete_link(buddies, buddies);
294 
295 		balias = purple_buddy_get_local_buddy_alias(b);
296 		if (purple_strequal(balias, alias))
297 			continue;
298 
299 		purple_blist_alias_buddy(b, alias);
300 	}
301 }
302 
303 
purple_get_attention_type_from_code(PurpleAccount * account,guint type_code)304 PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code)
305 {
306 	PurplePlugin *prpl;
307 	PurpleAttentionType* attn;
308 	GList *(*get_attention_types)(PurpleAccount *);
309 
310 	g_return_val_if_fail(account != NULL, NULL);
311 
312 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
313 
314 	/* Lookup the attention type in the protocol's attention_types list, if any. */
315 	get_attention_types = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->get_attention_types;
316 	if (get_attention_types) {
317 		GList *attention_types;
318 
319 		attention_types = get_attention_types(account);
320 		attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code);
321 	} else {
322 		attn = NULL;
323 	}
324 
325 	return attn;
326 }
327 
328 void
serv_send_attention(PurpleConnection * gc,const char * who,guint type_code)329 serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
330 {
331 	purple_prpl_send_attention(gc, who, type_code);
332 }
333 
334 void
serv_got_attention(PurpleConnection * gc,const char * who,guint type_code)335 serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
336 {
337 	purple_prpl_got_attention(gc, who, type_code);
338 }
339 
340 
341 /*
342  * Move a buddy from one group to another on server.
343  *
344  * Note: For now we'll not deal with changing gc's at the same time, but
345  * it should be possible.  Probably needs to be done, someday.  Although,
346  * the UI for that would be difficult, because groups are Purple-wide.
347  */
serv_move_buddy(PurpleBuddy * b,PurpleGroup * og,PurpleGroup * ng)348 void serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
349 {
350 	PurpleAccount *account;
351 	PurpleConnection *gc;
352 	PurplePlugin *prpl;
353 	PurplePluginProtocolInfo *prpl_info;
354 
355 	g_return_if_fail(b != NULL);
356 	g_return_if_fail(og != NULL);
357 	g_return_if_fail(ng != NULL);
358 
359 	account = purple_buddy_get_account(b);
360 	gc = purple_account_get_connection(account);
361 
362 	if (gc) {
363 		prpl = purple_connection_get_prpl(gc);
364 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
365 
366 		if (prpl_info->group_buddy)
367 			prpl_info->group_buddy(gc, purple_buddy_get_name(b),
368 					purple_group_get_name(og),
369 					purple_group_get_name(ng));
370 	}
371 }
372 
serv_add_permit(PurpleConnection * gc,const char * name)373 void serv_add_permit(PurpleConnection *gc, const char *name)
374 {
375 	PurplePlugin *prpl;
376 	PurplePluginProtocolInfo *prpl_info;
377 
378 	if (gc) {
379 		prpl = purple_connection_get_prpl(gc);
380 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
381 
382 		if (prpl_info->add_permit)
383 			prpl_info->add_permit(gc, name);
384 	}
385 }
386 
serv_add_deny(PurpleConnection * gc,const char * name)387 void serv_add_deny(PurpleConnection *gc, const char *name)
388 {
389 	PurplePlugin *prpl;
390 	PurplePluginProtocolInfo *prpl_info;
391 
392 	if (gc) {
393 		prpl = purple_connection_get_prpl(gc);
394 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
395 
396 		if (prpl_info->add_deny)
397 			prpl_info->add_deny(gc, name);
398 	}
399 }
400 
serv_rem_permit(PurpleConnection * gc,const char * name)401 void serv_rem_permit(PurpleConnection *gc, const char *name)
402 {
403 	PurplePlugin *prpl;
404 	PurplePluginProtocolInfo *prpl_info;
405 
406 	if (gc) {
407 		prpl = purple_connection_get_prpl(gc);
408 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
409 
410 		if (prpl_info->rem_permit)
411 			prpl_info->rem_permit(gc, name);
412 	}
413 }
414 
serv_rem_deny(PurpleConnection * gc,const char * name)415 void serv_rem_deny(PurpleConnection *gc, const char *name)
416 {
417 	PurplePlugin *prpl;
418 	PurplePluginProtocolInfo *prpl_info;
419 
420 	if (gc) {
421 		prpl = purple_connection_get_prpl(gc);
422 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
423 
424 		if (prpl_info->rem_deny)
425 			prpl_info->rem_deny(gc, name);
426 	}
427 }
428 
serv_set_permit_deny(PurpleConnection * gc)429 void serv_set_permit_deny(PurpleConnection *gc)
430 {
431 	PurplePlugin *prpl;
432 	PurplePluginProtocolInfo *prpl_info;
433 
434 	if (gc) {
435 		prpl = purple_connection_get_prpl(gc);
436 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
437 
438 		/*
439 		 * this is called when either you import a buddy list, and make lots
440 		 * of changes that way, or when the user toggles the permit/deny mode
441 		 * in the prefs. In either case you should probably be resetting and
442 		 * resending the permit/deny info when you get this.
443 		 */
444 		if (prpl_info->set_permit_deny)
445 			prpl_info->set_permit_deny(gc);
446 	}
447 }
448 
serv_join_chat(PurpleConnection * gc,GHashTable * data)449 void serv_join_chat(PurpleConnection *gc, GHashTable *data)
450 {
451 	PurplePlugin *prpl;
452 	PurplePluginProtocolInfo *prpl_info;
453 
454 	if (gc) {
455 		prpl = purple_connection_get_prpl(gc);
456 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
457 
458 		if (prpl_info->join_chat)
459 			prpl_info->join_chat(gc, data);
460 	}
461 }
462 
463 
serv_reject_chat(PurpleConnection * gc,GHashTable * data)464 void serv_reject_chat(PurpleConnection *gc, GHashTable *data)
465 {
466 	PurplePlugin *prpl;
467 	PurplePluginProtocolInfo *prpl_info;
468 
469 	if (gc) {
470 		prpl = purple_connection_get_prpl(gc);
471 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
472 
473 		if (prpl_info->reject_chat)
474 			prpl_info->reject_chat(gc, data);
475 	}
476 }
477 
serv_chat_invite(PurpleConnection * gc,int id,const char * message,const char * name)478 void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
479 {
480 	PurplePlugin *prpl = NULL;
481 	PurplePluginProtocolInfo *prpl_info = NULL;
482 	PurpleConversation *conv;
483 	char *buffy = message && *message ? g_strdup(message) : NULL;
484 
485 	conv = purple_find_chat(gc, id);
486 
487 	if(conv == NULL)
488 		return;
489 
490 	if(gc)
491 		prpl = purple_connection_get_prpl(gc);
492 
493 	if(prpl)
494 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
495 
496 	purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
497 					 conv, name, &buffy);
498 
499 	if (prpl_info && prpl_info->chat_invite)
500 		prpl_info->chat_invite(gc, id, buffy, name);
501 
502 	purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
503 					 conv, name, buffy);
504 
505 	g_free(buffy);
506 }
507 
508 /* Ya know, nothing uses this except purple_conversation_destroy(),
509  * I think I'll just merge it into that later...
510  * Then again, something might want to use this, from outside prpl-land
511  * to leave a chat without destroying the conversation.
512  */
serv_chat_leave(PurpleConnection * gc,int id)513 void serv_chat_leave(PurpleConnection *gc, int id)
514 {
515 	PurplePlugin *prpl;
516 	PurplePluginProtocolInfo *prpl_info;
517 
518 	prpl = purple_connection_get_prpl(gc);
519 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
520 
521 	if (prpl_info->chat_leave)
522 		prpl_info->chat_leave(gc, id);
523 }
524 
serv_chat_whisper(PurpleConnection * gc,int id,const char * who,const char * message)525 void serv_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message)
526 {
527 	PurplePlugin *prpl;
528 	PurplePluginProtocolInfo *prpl_info;
529 
530 	if (gc) {
531 		prpl = purple_connection_get_prpl(gc);
532 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
533 
534 		if (prpl_info->chat_whisper)
535 			prpl_info->chat_whisper(gc, id, who, message);
536 	}
537 }
538 
serv_chat_send(PurpleConnection * gc,int id,const char * message,PurpleMessageFlags flags)539 int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
540 {
541 	PurplePlugin *prpl;
542 	PurplePluginProtocolInfo *prpl_info;
543 
544 	prpl = purple_connection_get_prpl(gc);
545 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
546 
547 	if (prpl_info->chat_send)
548 		return prpl_info->chat_send(gc, id, message, flags);
549 
550 	return -EINVAL;
551 }
552 
553 /*
554  * woo. i'm actually going to comment this function. isn't that fun. make
555  * sure to follow along, kids
556  */
serv_got_im(PurpleConnection * gc,const char * who,const char * msg,PurpleMessageFlags flags,time_t mtime)557 void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
558 				 PurpleMessageFlags flags, time_t mtime)
559 {
560 	PurpleAccount *account;
561 	PurpleConversation *conv;
562 	char *message, *name;
563 	char *angel, *buffy;
564 	int plugin_return;
565 
566 	g_return_if_fail(msg != NULL);
567 
568 	account  = purple_connection_get_account(gc);
569 
570 	if (mtime < 0) {
571 		purple_debug_error("server",
572 				"serv_got_im ignoring negative timestamp\n");
573 		/* TODO: Would be more appropriate to use a value that indicates
574 		   that the timestamp is unknown, and surface that in the UI. */
575 		mtime = time(NULL);
576 	}
577 
578 	/*
579 	 * XXX: Should we be setting this here, or relying on prpls to set it?
580 	 */
581 	flags |= PURPLE_MESSAGE_RECV;
582 
583 	if (!purple_privacy_check(account, who)) {
584 		purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg",
585 				account, who, msg, flags, (unsigned int)mtime);
586 		return;
587 	}
588 
589 	/*
590 	 * We should update the conversation window buttons and menu,
591 	 * if it exists.
592 	 */
593 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, gc->account);
594 
595 	/*
596 	 * Make copies of the message and the sender in case plugins want
597 	 * to free these strings and replace them with a modifed version.
598 	 */
599 	buffy = g_strdup(msg);
600 	angel = g_strdup(who);
601 
602 	plugin_return = GPOINTER_TO_INT(
603 		purple_signal_emit_return_1(purple_conversations_get_handle(),
604 								  "receiving-im-msg", gc->account,
605 								  &angel, &buffy, conv, &flags));
606 
607 	if (!buffy || !angel || plugin_return) {
608 		g_free(buffy);
609 		g_free(angel);
610 		return;
611 	}
612 
613 	name = angel;
614 	message = buffy;
615 
616 	purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", gc->account,
617 					 name, message, conv, flags);
618 
619 	/* search for conversation again in case it was created by received-im-msg handler */
620 	if (conv == NULL)
621 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
622 
623 	if (conv == NULL)
624 		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
625 
626 	purple_conv_im_write(PURPLE_CONV_IM(conv), name, message, flags, mtime);
627 	g_free(message);
628 
629 	/*
630 	 * Don't autorespond if:
631 	 *
632 	 *  - it's not supported on this connection
633 	 *  - we are available
634 	 *  - or it's disabled
635 	 *  - or we're not idle and the 'only auto respond if idle' pref
636 	 *    is set
637 	 */
638 	if (gc->flags & PURPLE_CONNECTION_AUTO_RESP)
639 	{
640 		PurplePresence *presence;
641 		PurpleStatus *status;
642 		PurpleStatusType *status_type;
643 		PurpleStatusPrimitive primitive;
644 		const gchar *auto_reply_pref;
645 		const char *away_msg = NULL;
646 		gboolean mobile = FALSE;
647 
648 		auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
649 
650 		presence = purple_account_get_presence(account);
651 		status = purple_presence_get_active_status(presence);
652 		status_type = purple_status_get_type(status);
653 		primitive = purple_status_type_get_primitive(status_type);
654 		mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
655 		if ((primitive == PURPLE_STATUS_AVAILABLE) ||
656 			(primitive == PURPLE_STATUS_INVISIBLE) ||
657 			mobile ||
658 		    purple_strequal(auto_reply_pref, "never") ||
659 		    (!purple_presence_is_idle(presence) && purple_strequal(auto_reply_pref, "awayidle")))
660 		{
661 			g_free(name);
662 			return;
663 		}
664 
665 		away_msg = purple_value_get_string(
666 			purple_status_get_attr_value(status, "message"));
667 
668 		if ((away_msg != NULL) && (*away_msg != '\0')) {
669 			struct last_auto_response *lar;
670 			time_t now = time(NULL);
671 
672 			/*
673 			 * This used to be based on the conversation window. But um, if
674 			 * you went away, and someone sent you a message and got your
675 			 * auto-response, and then you closed the window, and then they
676 			 * sent you another one, they'd get the auto-response back too
677 			 * soon. Besides that, we need to keep track of this even if we've
678 			 * got a queue. So the rest of this block is just the auto-response,
679 			 * if necessary.
680 			 */
681 			lar = get_last_auto_response(gc, name);
682 			if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE)
683 			{
684 				/*
685 				 * We don't want to send an autoresponse in response to the other user's
686 				 * autoresponse.  We do, however, not want to then send one in response to the
687 				 * _next_ message, so we still set lar->sent to now.
688 				 */
689 				lar->sent = now;
690 
691 				if (!(flags & PURPLE_MESSAGE_AUTO_RESP))
692 				{
693 					serv_send_im(gc, name, away_msg, PURPLE_MESSAGE_AUTO_RESP);
694 
695 					purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, away_msg,
696 									   PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
697 									   mtime);
698 				}
699 			}
700 		}
701 	}
702 
703 	g_free(name);
704 }
705 
serv_got_typing(PurpleConnection * gc,const char * name,int timeout,PurpleTypingState state)706 void serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
707 					 PurpleTypingState state) {
708 	PurpleConversation *conv;
709 	PurpleConvIm *im = NULL;
710 
711 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
712 	if (conv != NULL) {
713 		im = PURPLE_CONV_IM(conv);
714 
715 		purple_conv_im_set_typing_state(im, state);
716 	} else {
717 		switch (state)
718 		{
719 			case PURPLE_TYPING:
720 				purple_signal_emit(purple_conversations_get_handle(),
721 								   "buddy-typing", gc->account, name);
722 				break;
723 			case PURPLE_TYPED:
724 				purple_signal_emit(purple_conversations_get_handle(),
725 								   "buddy-typed", gc->account, name);
726 				break;
727 			case PURPLE_NOT_TYPING:
728 				purple_signal_emit(purple_conversations_get_handle(),
729 								   "buddy-typing-stopped", gc->account, name);
730 				break;
731 		}
732 	}
733 
734 	if (conv != NULL && timeout > 0)
735 		purple_conv_im_start_typing_timeout(im, timeout);
736 }
737 
serv_got_typing_stopped(PurpleConnection * gc,const char * name)738 void serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
739 
740 	PurpleConversation *conv;
741 	PurpleConvIm *im;
742 
743 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
744 	if (conv != NULL)
745 	{
746 		im = PURPLE_CONV_IM(conv);
747 
748 		if (im->typing_state == PURPLE_NOT_TYPING)
749 			return;
750 
751 		purple_conv_im_stop_typing_timeout(im);
752 		purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
753 	}
754 	else
755 	{
756 		purple_signal_emit(purple_conversations_get_handle(),
757 						 "buddy-typing-stopped", gc->account, name);
758 	}
759 }
760 
761 struct chat_invite_data {
762 	PurpleConnection *gc;
763 	GHashTable *components;
764 };
765 
chat_invite_data_free(struct chat_invite_data * cid)766 static void chat_invite_data_free(struct chat_invite_data *cid)
767 {
768 	if (cid->components)
769 		g_hash_table_destroy(cid->components);
770 	g_free(cid);
771 }
772 
773 
chat_invite_reject(struct chat_invite_data * cid)774 static void chat_invite_reject(struct chat_invite_data *cid)
775 {
776 	serv_reject_chat(cid->gc, cid->components);
777 	chat_invite_data_free(cid);
778 }
779 
780 
chat_invite_accept(struct chat_invite_data * cid)781 static void chat_invite_accept(struct chat_invite_data *cid)
782 {
783 	serv_join_chat(cid->gc, cid->components);
784 	chat_invite_data_free(cid);
785 }
786 
787 
788 
serv_got_chat_invite(PurpleConnection * gc,const char * name,const char * who,const char * message,GHashTable * data)789 void serv_got_chat_invite(PurpleConnection *gc, const char *name,
790 						  const char *who, const char *message, GHashTable *data)
791 {
792 	PurpleAccount *account;
793 	struct chat_invite_data *cid;
794 	int plugin_return;
795 
796 	g_return_if_fail(name != NULL);
797 	g_return_if_fail(who != NULL);
798 
799 	account = purple_connection_get_account(gc);
800 	if (!purple_privacy_check(account, who)) {
801 		purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked",
802 				account, who, name, message, data);
803 		return;
804 	}
805 
806 	cid = g_new0(struct chat_invite_data, 1);
807 
808 	plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
809 					purple_conversations_get_handle(),
810 					"chat-invited", account, who, name, message, data));
811 
812 	cid->gc = gc;
813 	cid->components = data;
814 
815 	if (plugin_return == 0)
816 	{
817 		char *buf2;
818 
819 		if (message != NULL)
820 		{
821 			buf2 = g_strdup_printf(
822 				   _("%s has invited %s to the chat room %s:\n%s"),
823 				   who, purple_account_get_username(account), name, message);
824 		}
825 		else
826 			buf2 = g_strdup_printf(
827 				   _("%s has invited %s to the chat room %s\n"),
828 				   who, purple_account_get_username(account), name);
829 
830 
831 		purple_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2,
832 							   PURPLE_DEFAULT_ACTION_NONE, account, who, NULL,
833 							   cid, G_CALLBACK(chat_invite_accept),
834 							   G_CALLBACK(chat_invite_reject));
835 
836 		g_free(buf2);
837 	}
838 	else if (plugin_return > 0)
839 		chat_invite_accept(cid);
840 	else
841 		chat_invite_reject(cid);
842 }
843 
serv_got_joined_chat(PurpleConnection * gc,int id,const char * name)844 PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
845 											   int id, const char *name)
846 {
847 	PurpleConversation *conv;
848 	PurpleConvChat *chat;
849 	PurpleAccount *account;
850 
851 	account = purple_connection_get_account(gc);
852 
853 	g_return_val_if_fail(account != NULL, NULL);
854 	g_return_val_if_fail(name != NULL, NULL);
855 
856 	conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
857 	g_return_val_if_fail(conv != NULL, NULL);
858 
859 	chat = PURPLE_CONV_CHAT(conv);
860 
861 	if (!g_slist_find(gc->buddy_chats, conv))
862 		gc->buddy_chats = g_slist_append(gc->buddy_chats, conv);
863 
864 	purple_conv_chat_set_id(chat, id);
865 
866 	purple_signal_emit(purple_conversations_get_handle(), "chat-joined", conv);
867 
868 	return conv;
869 }
870 
serv_got_chat_left(PurpleConnection * g,int id)871 void serv_got_chat_left(PurpleConnection *g, int id)
872 {
873 	GSList *bcs;
874 	PurpleConversation *conv = NULL;
875 	PurpleConvChat *chat = NULL;
876 
877 	for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
878 		conv = (PurpleConversation *)bcs->data;
879 
880 		chat = PURPLE_CONV_CHAT(conv);
881 
882 		if (purple_conv_chat_get_id(chat) == id)
883 			break;
884 
885 		conv = NULL;
886 	}
887 
888 	if (!conv)
889 		return;
890 
891 	purple_debug(PURPLE_DEBUG_INFO, "server", "Leaving room: %s\n",
892 			   purple_conversation_get_name(conv));
893 
894 	g->buddy_chats = g_slist_remove(g->buddy_chats, conv);
895 
896 	purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
897 
898 	purple_signal_emit(purple_conversations_get_handle(), "chat-left", conv);
899 }
900 
purple_serv_got_join_chat_failed(PurpleConnection * gc,GHashTable * data)901 void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
902 {
903 	purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed",
904 					gc, data);
905 }
906 
serv_got_chat_in(PurpleConnection * g,int id,const char * who,PurpleMessageFlags flags,const char * message,time_t mtime)907 void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
908 					  PurpleMessageFlags flags, const char *message, time_t mtime)
909 {
910 	GSList *bcs;
911 	PurpleConversation *conv = NULL;
912 	PurpleConvChat *chat = NULL;
913 	char *buffy, *angel;
914 	int plugin_return;
915 
916 	g_return_if_fail(who != NULL);
917 	g_return_if_fail(message != NULL);
918 
919 	if (mtime < 0) {
920 		purple_debug_error("server",
921 				"serv_got_chat_in ignoring negative timestamp\n");
922 		/* TODO: Would be more appropriate to use a value that indicates
923 		   that the timestamp is unknown, and surface that in the UI. */
924 		mtime = time(NULL);
925 	}
926 
927 	for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
928 		conv = (PurpleConversation *)bcs->data;
929 
930 		chat = PURPLE_CONV_CHAT(conv);
931 
932 		if (purple_conv_chat_get_id(chat) == id)
933 			break;
934 
935 		conv = NULL;
936 	}
937 
938 	if (!conv)
939 		return;
940 
941 	/* Did I send the message? */
942 	if (purple_strequal(purple_conv_chat_get_nick(chat),
943 				purple_normalize(purple_conversation_get_account(conv), who))) {
944 		flags |= PURPLE_MESSAGE_SEND;
945 		flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */
946 	} else {
947 		flags |= PURPLE_MESSAGE_RECV;
948 	}
949 
950 	/*
951 	 * Make copies of the message and the sender in case plugins want
952 	 * to free these strings and replace them with a modifed version.
953 	 */
954 	buffy = g_strdup(message);
955 	angel = g_strdup(who);
956 
957 	plugin_return = GPOINTER_TO_INT(
958 		purple_signal_emit_return_1(purple_conversations_get_handle(),
959 								  "receiving-chat-msg", g->account,
960 								  &angel, &buffy, conv, &flags));
961 
962 	if (!buffy || !angel || plugin_return) {
963 		g_free(buffy);
964 		g_free(angel);
965 		return;
966 	}
967 
968 	who = angel;
969 	message = buffy;
970 
971 	purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", g->account,
972 					 who, message, conv, flags);
973 
974 	purple_conv_chat_write(chat, who, message, flags, mtime);
975 
976 	g_free(angel);
977 	g_free(buffy);
978 }
979 
serv_send_file(PurpleConnection * gc,const char * who,const char * file)980 void serv_send_file(PurpleConnection *gc, const char *who, const char *file)
981 {
982 	PurplePlugin *prpl;
983 	PurplePluginProtocolInfo *prpl_info;
984 
985 	g_return_if_fail(gc != NULL);
986 
987 	prpl = purple_connection_get_prpl(gc);
988 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
989 
990 	if (prpl_info->send_file && (!prpl_info->can_receive_file
991 				     || prpl_info->can_receive_file(gc, who))) {
992 		prpl_info->send_file(gc, who, file);
993 	}
994 }
995 
serv_chat_send_file(PurpleConnection * gc,int id,const char * file)996 void serv_chat_send_file(PurpleConnection *gc, int id, const char *file)
997 {
998 	PurplePlugin *prpl;
999 	PurplePluginProtocolInfo *prpl_info;
1000 
1001 	g_return_if_fail(gc != NULL);
1002 
1003 	prpl = purple_connection_get_prpl(gc);
1004 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
1005 
1006 	if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_send_file) &&
1007 	    (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_can_receive_file)
1008 	     || prpl_info->chat_can_receive_file(gc, id))) {
1009 		prpl_info->chat_send_file(gc, id, file);
1010 	}
1011 }
1012