Fix a crash when remote users have certain characters in their
[pidgin-git.git] / libpurple / privacy.c
blob00dd7e449379b931abbc1ff733d6571f5a3f0882
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
22 #include "internal.h"
24 #include "account.h"
25 #include "privacy.h"
26 #include "server.h"
27 #include "util.h"
29 static PurplePrivacyUiOps *privacy_ops = NULL;
31 gboolean
32 purple_privacy_permit_add(PurpleAccount *account, const char *who,
33 gboolean local_only)
35 GSList *l;
36 char *name;
37 PurpleBuddy *buddy;
38 PurpleBlistUiOps *blist_ops;
40 g_return_val_if_fail(account != NULL, FALSE);
41 g_return_val_if_fail(who != NULL, FALSE);
43 name = g_strdup(purple_normalize(account, who));
45 for (l = account->permit; l != NULL; l = l->next) {
46 if (g_str_equal(name, l->data))
47 /* This buddy already exists */
48 break;
51 if (l != NULL)
53 /* This buddy already exists, so bail out */
54 g_free(name);
55 return FALSE;
58 account->permit = g_slist_append(account->permit, name);
60 if (!local_only && purple_account_is_connected(account))
61 serv_add_permit(purple_account_get_connection(account), who);
63 if (privacy_ops != NULL && privacy_ops->permit_added != NULL)
64 privacy_ops->permit_added(account, who);
66 blist_ops = purple_blist_get_ui_ops();
67 if (blist_ops != NULL && blist_ops->save_account != NULL)
68 blist_ops->save_account(account);
70 /* This lets the UI know a buddy has had its privacy setting changed */
71 buddy = purple_find_buddy(account, name);
72 if (buddy != NULL) {
73 purple_signal_emit(purple_blist_get_handle(),
74 "buddy-privacy-changed", buddy);
76 return TRUE;
79 gboolean
80 purple_privacy_permit_remove(PurpleAccount *account, const char *who,
81 gboolean local_only)
83 GSList *l;
84 const char *name;
85 PurpleBuddy *buddy;
86 char *del;
87 PurpleBlistUiOps *blist_ops;
89 g_return_val_if_fail(account != NULL, FALSE);
90 g_return_val_if_fail(who != NULL, FALSE);
92 name = purple_normalize(account, who);
94 for (l = account->permit; l != NULL; l = l->next) {
95 if (g_str_equal(name, l->data))
96 /* We found the buddy we were looking for */
97 break;
100 if (l == NULL)
101 /* We didn't find the buddy we were looking for, so bail out */
102 return FALSE;
104 /* We should not free l->data just yet. There can be occasions where
105 * l->data == who. In such cases, freeing l->data here can cause crashes
106 * later when who is used. */
107 del = l->data;
108 account->permit = g_slist_delete_link(account->permit, l);
110 if (!local_only && purple_account_is_connected(account))
111 serv_rem_permit(purple_account_get_connection(account), who);
113 if (privacy_ops != NULL && privacy_ops->permit_removed != NULL)
114 privacy_ops->permit_removed(account, who);
116 blist_ops = purple_blist_get_ui_ops();
117 if (blist_ops != NULL && blist_ops->save_account != NULL)
118 blist_ops->save_account(account);
120 buddy = purple_find_buddy(account, name);
121 if (buddy != NULL) {
122 purple_signal_emit(purple_blist_get_handle(),
123 "buddy-privacy-changed", buddy);
125 g_free(del);
126 return TRUE;
129 gboolean
130 purple_privacy_deny_add(PurpleAccount *account, const char *who,
131 gboolean local_only)
133 GSList *l;
134 char *name;
135 PurpleBuddy *buddy;
136 PurpleBlistUiOps *blist_ops;
138 g_return_val_if_fail(account != NULL, FALSE);
139 g_return_val_if_fail(who != NULL, FALSE);
141 name = g_strdup(purple_normalize(account, who));
143 for (l = account->deny; l != NULL; l = l->next) {
144 if (g_str_equal(name, l->data))
145 /* This buddy already exists */
146 break;
149 if (l != NULL)
151 /* This buddy already exists, so bail out */
152 g_free(name);
153 return FALSE;
156 account->deny = g_slist_append(account->deny, name);
158 if (!local_only && purple_account_is_connected(account))
159 serv_add_deny(purple_account_get_connection(account), who);
161 if (privacy_ops != NULL && privacy_ops->deny_added != NULL)
162 privacy_ops->deny_added(account, who);
164 blist_ops = purple_blist_get_ui_ops();
165 if (blist_ops != NULL && blist_ops->save_account != NULL)
166 blist_ops->save_account(account);
168 buddy = purple_find_buddy(account, name);
169 if (buddy != NULL) {
170 purple_signal_emit(purple_blist_get_handle(),
171 "buddy-privacy-changed", buddy);
173 return TRUE;
176 gboolean
177 purple_privacy_deny_remove(PurpleAccount *account, const char *who,
178 gboolean local_only)
180 GSList *l;
181 const char *normalized;
182 char *name;
183 PurpleBuddy *buddy;
184 PurpleBlistUiOps *blist_ops;
186 g_return_val_if_fail(account != NULL, FALSE);
187 g_return_val_if_fail(who != NULL, FALSE);
189 normalized = purple_normalize(account, who);
191 for (l = account->deny; l != NULL; l = l->next) {
192 if (g_str_equal(normalized, l->data))
193 /* We found the buddy we were looking for */
194 break;
197 if (l == NULL)
198 /* We didn't find the buddy we were looking for, so bail out */
199 return FALSE;
201 buddy = purple_find_buddy(account, normalized);
203 name = l->data;
204 account->deny = g_slist_delete_link(account->deny, l);
206 if (!local_only && purple_account_is_connected(account))
207 serv_rem_deny(purple_account_get_connection(account), name);
209 if (privacy_ops != NULL && privacy_ops->deny_removed != NULL)
210 privacy_ops->deny_removed(account, who);
212 if (buddy != NULL) {
213 purple_signal_emit(purple_blist_get_handle(),
214 "buddy-privacy-changed", buddy);
217 g_free(name);
219 blist_ops = purple_blist_get_ui_ops();
220 if (blist_ops != NULL && blist_ops->save_account != NULL)
221 blist_ops->save_account(account);
223 return TRUE;
227 * This makes sure your permit list contains all buddies from your
228 * buddy list and ONLY buddies from your buddy list.
230 static void
231 add_all_buddies_to_permit_list(PurpleAccount *account, gboolean local)
233 GSList *list;
235 /* Remove anyone in the permit list who is not in the buddylist */
236 for (list = account->permit; list != NULL; ) {
237 char *person = list->data;
238 list = list->next;
239 if (!purple_find_buddy(account, person))
240 purple_privacy_permit_remove(account, person, local);
243 /* Now make sure everyone in the buddylist is in the permit list */
244 list = purple_find_buddies(account, NULL);
245 while (list != NULL)
247 PurpleBuddy *buddy = list->data;
248 const gchar *name = purple_buddy_get_name(buddy);
250 if (!g_slist_find_custom(account->permit, name, (GCompareFunc)g_utf8_collate))
251 purple_privacy_permit_add(account, name, local);
252 list = g_slist_delete_link(list, list);
257 * TODO: All callers of this function pass in FALSE for local and
258 * restore and I don't understand when you would ever want to
259 * use TRUE for either of them. I think both parameters could
260 * safely be removed in the next major version bump.
262 void
263 purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
264 gboolean restore)
266 GSList *list;
267 PurplePrivacyType type = account->perm_deny;
269 switch (account->perm_deny) {
270 case PURPLE_PRIVACY_ALLOW_ALL:
271 return;
272 case PURPLE_PRIVACY_ALLOW_USERS:
273 purple_privacy_permit_add(account, who, local);
274 break;
275 case PURPLE_PRIVACY_DENY_USERS:
276 purple_privacy_deny_remove(account, who, local);
277 break;
278 case PURPLE_PRIVACY_DENY_ALL:
279 if (!restore) {
280 /* Empty the allow-list. */
281 const char *norm = purple_normalize(account, who);
282 for (list = account->permit; list != NULL;) {
283 char *person = list->data;
284 list = list->next;
285 if (!purple_strequal(norm, person))
286 purple_privacy_permit_remove(account, person, local);
289 purple_privacy_permit_add(account, who, local);
290 account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
291 break;
292 case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
293 if (!purple_find_buddy(account, who)) {
294 add_all_buddies_to_permit_list(account, local);
295 purple_privacy_permit_add(account, who, local);
296 account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
298 break;
299 default:
300 g_return_if_reached();
303 /* Notify the server if the privacy setting was changed */
304 if (type != account->perm_deny && purple_account_is_connected(account))
305 serv_set_permit_deny(purple_account_get_connection(account));
309 * TODO: All callers of this function pass in FALSE for local and
310 * restore and I don't understand when you would ever want to
311 * use TRUE for either of them. I think both parameters could
312 * safely be removed in the next major version bump.
314 void
315 purple_privacy_deny(PurpleAccount *account, const char *who, gboolean local,
316 gboolean restore)
318 GSList *list;
319 PurplePrivacyType type = account->perm_deny;
321 switch (account->perm_deny) {
322 case PURPLE_PRIVACY_ALLOW_ALL:
323 if (!restore) {
324 /* Empty the deny-list. */
325 const char *norm = purple_normalize(account, who);
326 for (list = account->deny; list != NULL; ) {
327 char *person = list->data;
328 list = list->next;
329 if (!purple_strequal(norm, person))
330 purple_privacy_deny_remove(account, person, local);
333 purple_privacy_deny_add(account, who, local);
334 account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
335 break;
336 case PURPLE_PRIVACY_ALLOW_USERS:
337 purple_privacy_permit_remove(account, who, local);
338 break;
339 case PURPLE_PRIVACY_DENY_USERS:
340 purple_privacy_deny_add(account, who, local);
341 break;
342 case PURPLE_PRIVACY_DENY_ALL:
343 break;
344 case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
345 if (purple_find_buddy(account, who)) {
346 add_all_buddies_to_permit_list(account, local);
347 purple_privacy_permit_remove(account, who, local);
348 account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
350 break;
351 default:
352 g_return_if_reached();
355 /* Notify the server if the privacy setting was changed */
356 if (type != account->perm_deny && purple_account_is_connected(account))
357 serv_set_permit_deny(purple_account_get_connection(account));
360 gboolean
361 purple_privacy_check(PurpleAccount *account, const char *who)
363 GSList *list;
365 switch (account->perm_deny) {
366 case PURPLE_PRIVACY_ALLOW_ALL:
367 return TRUE;
369 case PURPLE_PRIVACY_DENY_ALL:
370 return FALSE;
372 case PURPLE_PRIVACY_ALLOW_USERS:
373 who = purple_normalize(account, who);
374 for (list=account->permit; list!=NULL; list=list->next) {
375 if (g_str_equal(who, list->data))
376 return TRUE;
378 return FALSE;
380 case PURPLE_PRIVACY_DENY_USERS:
381 who = purple_normalize(account, who);
382 for (list=account->deny; list!=NULL; list=list->next) {
383 if (g_str_equal(who, list->data))
384 return FALSE;
386 return TRUE;
388 case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
389 return (purple_find_buddy(account, who) != NULL);
391 default:
392 g_return_val_if_reached(TRUE);
396 void
397 purple_privacy_set_ui_ops(PurplePrivacyUiOps *ops)
399 privacy_ops = ops;
402 PurplePrivacyUiOps *
403 purple_privacy_get_ui_ops(void)
405 return privacy_ops;
408 void
409 purple_privacy_init(void)