1 /*
2  *   Discord plugin for libpurple
3  *   Copyright (C) 2016-2017 Eion Robb
4  *
5  *   This program is free software: you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, either version 3 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #include <purple.h>
20 #if PURPLE_VERSION_CHECK(3, 0, 0)
21 #include <http.h>
22 #include <image.h>
23 #endif
24 
25 #ifndef PURPLE_PLUGINS
26 #define PURPLE_PLUGINS
27 #endif
28 
29 #ifdef _WIN32
30 #include <win32/win32dep.h>
31 #endif
32 
33 // Purple2 compat functions
34 #if !PURPLE_VERSION_CHECK(3, 0, 0)
35 #include "purple2compat/http.h"
36 #include "purple2compat/image.h"
37 #include "purple2compat/image-store.h"
38 
39 #define purple_buddy_get_local_alias  purple_buddy_get_local_buddy_alias
40 #define purple_buddy_set_local_alias  purple_blist_alias_buddy
41 #define purple_connection_error purple_connection_error_reason
42 #define purple_connection_get_protocol purple_connection_get_prpl
43 #define PURPLE_CONNECTION_CONNECTING PURPLE_CONNECTING
44 #define PURPLE_CONNECTION_CONNECTED PURPLE_CONNECTED
45 #define PURPLE_CONNECTION_FLAG_HTML PURPLE_CONNECTION_HTML
46 #define PURPLE_CONNECTION_FLAG_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR
47 #define PURPLE_CONNECTION_FLAG_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE
48 #define PURPLE_CONNECTION_FLAG_NO_IMAGES PURPLE_CONNECTION_NO_IMAGES
49 #define purple_connection_set_flags(pc, f) ((pc)->flags = (f))
50 #define purple_connection_get_flags(pc) ((pc)->flags)
51 #define purple_blist_find_group purple_find_group
52 #define purple_protocol_action_get_connection(action) ((PurpleConnection *) (action)->context)
53 #define purple_protocol_action_new purple_plugin_action_new
54 #define purple_protocol_get_id purple_plugin_get_id
55 #define PurpleProtocolAction PurplePluginAction
56 #define PurpleProtocolChatEntry struct proto_chat_entry
57 #define PurpleChatConversation PurpleConvChat
58 #define PurpleIMConversation PurpleConvIm
59 #define purple_conversations_find_chat_with_account(id, account) \
60 	PURPLE_CONV_CHAT(purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, id, account))
61 #define purple_chat_conversation_has_left purple_conv_chat_has_left
62 #define PurpleConversationUpdateType PurpleConvUpdateType
63 #define PURPLE_CONVERSATION_UPDATE_UNSEEN PURPLE_CONV_UPDATE_UNSEEN
64 #define PURPLE_IS_IM_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
65 #define PURPLE_IS_CHAT_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
66 #define PURPLE_CONVERSATION(chatorim) ((chatorim) == NULL ? NULL : (chatorim)->conv)
67 #define PURPLE_IM_CONVERSATION(conv) PURPLE_CONV_IM(conv)
68 #define PURPLE_CHAT_CONVERSATION(conv) PURPLE_CONV_CHAT(conv)
69 #define purple_conversation_present_error purple_conv_present_error
70 #define purple_serv_got_joined_chat(pc, id, name) PURPLE_CONV_CHAT(serv_got_joined_chat(pc, id, name))
71 #define purple_serv_got_chat_left serv_got_chat_left
72 static inline PurpleConvChat *
purple_conversations_find_chat(PurpleConnection * pc,int id)73 purple_conversations_find_chat(PurpleConnection *pc, int id)
74 {
75 	PurpleConversation *conv = purple_find_chat(pc, id);
76 
77 	if (conv != NULL) {
78 		return PURPLE_CONV_CHAT(conv);
79 	}
80 
81 	return NULL;
82 }
83 #define purple_serv_got_chat_in serv_got_chat_in
84 #define purple_chat_conversation_add_user purple_conv_chat_add_user
85 #define purple_chat_conversation_add_users purple_conv_chat_add_users
86 #define purple_chat_conversation_remove_user purple_conv_chat_remove_user
87 #define purple_chat_conversation_clear_users purple_conv_chat_clear_users
88 #define purple_chat_conversation_has_user purple_conv_chat_find_user
89 #define purple_chat_conversation_rename_user purple_conv_chat_rename_user
90 #define purple_chat_conversation_get_topic purple_conv_chat_get_topic
91 #define purple_chat_conversation_set_topic purple_conv_chat_set_topic
92 #define purple_chat_conversation_set_nick purple_conv_chat_set_nick
93 #define PurpleChatUserFlags PurpleConvChatBuddyFlags
94 #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE
95 #define PURPLE_CHAT_USER_OP PURPLE_CBFLAGS_OP
96 #define PURPLE_CHAT_USER_FOUNDER PURPLE_CBFLAGS_FOUNDER
97 #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
98 #define PURPLE_CHAT_USER_AWAY PURPLE_CBFLAGS_AWAY
99 #define PURPLE_CHAT_USER_HALFOP PURPLE_CBFLAGS_HALFOP
100 #define PURPLE_CHAT_USER_VOICE PURPLE_CBFLAGS_VOICE
101 #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
102 #define PurpleChatUser PurpleConvChatBuddy
103 static inline PurpleChatUser *
purple_chat_conversation_find_user(PurpleChatConversation * chat,const char * name)104 purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
105 {
106 	PurpleChatUser *cb = purple_conv_chat_cb_find(chat, name);
107 
108 	if (cb != NULL) {
109 		g_dataset_set_data(cb, "chat", chat);
110 	}
111 
112 	return cb;
113 }
114 #define purple_chat_user_get_flags(cb) purple_conv_chat_user_get_flags(g_dataset_get_data((cb), "chat"), (cb)->name)
115 #define purple_chat_user_set_flags(cb, f) purple_conv_chat_user_set_flags(g_dataset_get_data((cb), "chat"), (cb)->name, (f))
116 #define purple_chat_user_set_alias(cb, a) (g_free((cb)->alias), (cb)->alias = g_strdup(a))
117 #define PurpleIMTypingState PurpleTypingState
118 #define PURPLE_IM_NOT_TYPING PURPLE_NOT_TYPING
119 #define PURPLE_IM_TYPING PURPLE_TYPING
120 #define PURPLE_IM_TYPED PURPLE_TYPED
121 #define purple_conversation_get_connection purple_conversation_get_gc
122 #define purple_conversation_write_system_message(conv, message, flags) purple_conversation_write((conv), NULL, (message), ((flags) | PURPLE_MESSAGE_SYSTEM), time(NULL))
123 #define purple_chat_conversation_get_id purple_conv_chat_get_id
124 #define PURPLE_CMD_FLAG_PROTOCOL_ONLY PURPLE_CMD_FLAG_PRPL_ONLY
125 #define PURPLE_IS_BUDDY PURPLE_BLIST_NODE_IS_BUDDY
126 #define PURPLE_IS_CHAT PURPLE_BLIST_NODE_IS_CHAT
127 #define purple_chat_get_name_only purple_chat_get_name
128 #define purple_blist_find_buddy purple_find_buddy
129 #define purple_serv_got_alias serv_got_alias
130 #define purple_account_set_private_alias purple_account_set_alias
131 #define purple_account_get_private_alias purple_account_get_alias
132 #define purple_protocol_got_user_status purple_prpl_got_user_status
133 #define purple_protocol_got_user_idle purple_prpl_got_user_idle
134 #define purple_serv_got_im serv_got_im
135 #define purple_serv_got_typing serv_got_typing
136 #define purple_conversations_find_im_with_account(name, account) \
137 	PURPLE_CONV_IM(purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account))
138 #define purple_im_conversation_new(account, from) PURPLE_CONV_IM(purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from))
139 #define PurpleMessage PurpleConvMessage
140 #define purple_message_set_time(msg, time) ((msg)->when = (time))
141 #define purple_conversation_write_message(conv, msg) purple_conversation_write(conv, msg->who, msg->what, msg->flags, msg->when)
142 static inline PurpleMessage *
purple_message_new_outgoing(const gchar * who,const gchar * contents,PurpleMessageFlags flags)143 purple_message_new_outgoing(const gchar *who, const gchar *contents, PurpleMessageFlags flags)
144 {
145 	PurpleMessage *message = g_new0(PurpleMessage, 1);
146 
147 	message->who = g_strdup(who);
148 	message->what = g_strdup(contents);
149 	message->flags = flags;
150 	message->when = time(NULL);
151 
152 	return message;
153 }
154 static inline void
purple_message_destroy(PurpleMessage * message)155 purple_message_destroy(PurpleMessage *message)
156 {
157 	g_free(message->who);
158 	g_free(message->what);
159 	g_free(message);
160 }
161 
162 #define purple_message_get_recipient(message) (message->who)
163 #define purple_message_get_contents(message) (message->what)
164 #if !PURPLE_VERSION_CHECK(2, 12, 0)
165 #define PURPLE_MESSAGE_REMOTE_SEND 0x10000
166 #endif
167 
168 #define purple_proxy_info_get_proxy_type        purple_proxy_info_get_type
169 
170 #define purple_account_privacy_deny_add purple_privacy_deny_add
171 #define purple_account_privacy_deny_remove purple_privacy_deny_remove
172 #define purple_buddy_set_name purple_blist_rename_buddy
173 #define purple_request_cpar_from_connection(a) purple_connection_get_account(a), NULL, NULL
174 #define purple_notify_user_info_add_pair_html purple_notify_user_info_add_pair
175 
176 #ifdef purple_notify_error
177 #undef purple_notify_error
178 #endif
179 #define purple_notify_error(handle, title, primary, secondary, cpar)  \
180 	purple_notify_message((handle), PURPLE_NOTIFY_MSG_ERROR, (title), \
181 						  (primary), (secondary), NULL, NULL)
182 
183 
184 #define purple_conversation_get_smiley(conv, smiley)  (!purple_conv_custom_smiley_add((conv), (smiley), NULL, NULL, TRUE))
185 
186 static inline void
purple_conversation_add_smiley(PurpleConversation * conv,PurpleSmiley * smiley)187 purple_conversation_add_smiley(PurpleConversation *conv, PurpleSmiley *smiley)
188 {
189 	gsize len;
190 	const guchar *data;
191 	const gchar *shortcut;
192 
193 	data = purple_smiley_get_data(smiley, &len);
194 	shortcut = purple_smiley_get_shortcut(smiley);
195 
196 	purple_conv_custom_smiley_write(conv, shortcut, data, len);
197 	purple_conv_custom_smiley_close(conv, shortcut);
198 }
199 
200 static inline PurpleSmiley *
purple_smiley_new_from_data(const gchar * shortcut,const guchar * data,gsize size)201 purple_smiley_new_from_data(const gchar *shortcut, const guchar *data, gsize size)
202 {
203 	gchar *filename;
204 	PurpleStoredImage *stored_img;
205 
206 	g_return_val_if_fail(data != NULL, NULL);
207 	g_return_val_if_fail(size > 0, NULL);
208 
209 	filename = purple_util_get_image_filename(data, size);
210 	if (filename == NULL) {
211 		return NULL;
212 	}
213 
214 	stored_img = purple_imgstore_add(g_memdup(data, size), size, filename);
215 	g_free(filename);
216 
217 	return purple_smiley_new(stored_img, shortcut);
218 }
219 
220 // Kinda gross, since we can technically use the glib mainloop from purple2
221 #define g_timeout_add_seconds  purple_timeout_add_seconds
222 #define g_timeout_add          purple_timeout_add
223 #define g_source_remove        purple_timeout_remove
224 
225 #else
226 // Purple3 helper functions
227 #define purple_conversation_set_data(conv, key, value) g_object_set_data(G_OBJECT(conv), key, value)
228 #define purple_conversation_get_data(conv, key) g_object_get_data(G_OBJECT(conv), key)
229 #define purple_message_destroy g_object_unref
230 #define purple_chat_user_set_alias(cb, alias) g_object_set((cb), "alias", (alias), NULL)
231 #define purple_chat_get_alias(chat) g_object_get_data(G_OBJECT(chat), "alias")
232 #define purple_protocol_action_get_connection(action) ((action)->connection)
233 #define PURPLE_TYPE_STRING G_TYPE_STRING
234 //TODO remove this when dx adds this to the PurpleMessageFlags enum
235 #define PURPLE_MESSAGE_REMOTE_SEND 0x10000
236 #endif
237