mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
Type-check button arguments of msg_box.
Don't cast function pointers; calling functions via pointers of incorrect types is not guaranteed to work. Instead, define the functions with the desired types, and make them cast the incoming parameters. Or define wrapper functions if the return types don't match. really_exit_prog wasn't being used outside src/dialogs/menu.c, and I had to change its parameter type, so it's now static.
This commit is contained in:
parent
1e8a61e09b
commit
2b7788614f
@ -438,7 +438,7 @@ push_hierbox_info_button(struct dialog_data *dlg_data, struct widget_data *butto
|
||||
N_("Info"), ALIGN_LEFT,
|
||||
msg,
|
||||
context, 1,
|
||||
N_("~OK"), done_listbox_context, B_ESC | B_ENTER);
|
||||
MSG_BOX_BUTTON(N_("~OK"), done_listbox_context, B_ESC | B_ENTER));
|
||||
|
||||
return EVENT_PROCESSED;
|
||||
}
|
||||
@ -751,8 +751,8 @@ query_delete_selected_item(void *context_)
|
||||
listbox_message(delete_folder_title), ALIGN_CENTER,
|
||||
msg_text(term, listbox_message(delete_folder), text),
|
||||
context, 2,
|
||||
N_("~Yes"), push_ok_delete_button, B_ENTER,
|
||||
N_("~No"), done_listbox_context, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), done_listbox_context, B_ESC));
|
||||
} else {
|
||||
unsigned char *msg = ops->get_info(item, term);
|
||||
|
||||
@ -763,8 +763,8 @@ query_delete_selected_item(void *context_)
|
||||
msg_text(term, listbox_message(delete_item),
|
||||
text, empty_string_or_(msg)),
|
||||
context, 2,
|
||||
N_("~Yes"), push_ok_delete_button, B_ENTER,
|
||||
N_("~No"), done_listbox_context, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), done_listbox_context, B_ESC));
|
||||
mem_free_if(msg);
|
||||
}
|
||||
mem_free(text);
|
||||
@ -772,6 +772,12 @@ query_delete_selected_item(void *context_)
|
||||
return EVENT_PROCESSED;
|
||||
}
|
||||
|
||||
static void
|
||||
dont_delete_marked_items(void *const context_)
|
||||
{
|
||||
query_delete_selected_item(context_);
|
||||
}
|
||||
|
||||
widget_handler_status_T
|
||||
push_hierbox_delete_button(struct dialog_data *dlg_data,
|
||||
struct widget_data *button)
|
||||
@ -805,8 +811,8 @@ push_hierbox_delete_button(struct dialog_data *dlg_data,
|
||||
listbox_message(delete_marked_items_title), ALIGN_CENTER,
|
||||
listbox_message(delete_marked_items),
|
||||
context, 2,
|
||||
N_("~Yes"), push_ok_delete_button, B_ENTER,
|
||||
N_("~No"), query_delete_selected_item, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), push_ok_delete_button, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), dont_delete_marked_items, B_ESC));
|
||||
|
||||
return EVENT_PROCESSED;
|
||||
}
|
||||
@ -867,8 +873,8 @@ push_hierbox_clear_button(struct dialog_data *dlg_data,
|
||||
listbox_message(clear_all_items_title), ALIGN_CENTER,
|
||||
listbox_message(clear_all_items),
|
||||
context, 2,
|
||||
N_("~Yes"), do_clear_browser, B_ENTER,
|
||||
N_("~No"), NULL, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), do_clear_browser, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
|
||||
|
||||
return EVENT_PROCESSED;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ msg_box(struct terminal *term, struct memory_list *ml, enum msgbox_flags flags,
|
||||
int bflags;
|
||||
|
||||
label = va_arg(ap, unsigned char *);
|
||||
done = va_arg(ap, void *);
|
||||
done = va_arg(ap, done_handler_T *);
|
||||
bflags = va_arg(ap, int);
|
||||
|
||||
if (!label) {
|
||||
@ -170,7 +170,7 @@ refreshed_msg_box(struct terminal *term, enum msgbox_flags flags,
|
||||
title, align,
|
||||
info,
|
||||
data, 1,
|
||||
N_("~OK"), NULL, B_ENTER | B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC));
|
||||
|
||||
if (!dlg_data) return;
|
||||
|
||||
@ -187,6 +187,9 @@ info_box(struct terminal *term, enum msgbox_flags flags,
|
||||
unsigned char *text)
|
||||
{
|
||||
/* [gettext_accelerator_context(info_box)] */
|
||||
return msg_box(term, NULL, flags, title, align, text,
|
||||
NULL, 1, N_("~OK"), NULL, B_ENTER | B_ESC);
|
||||
return msg_box(term, NULL, flags,
|
||||
title, align,
|
||||
text,
|
||||
NULL, 1,
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC));
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ enum msgbox_flags {
|
||||
* @udata Is a reference to any data that should be passed to
|
||||
* the handlers associated with each button. NULL if none.
|
||||
*
|
||||
* @buttons Denotes the number of buttons given as varadic arguments.
|
||||
* @buttons Denotes the number of buttons given as variadic arguments.
|
||||
* For each button 3 arguments are extracted:
|
||||
* o First the label text. It is automatically localized
|
||||
* unless MSGBOX_NO_INTL is passed. If NULL, this button
|
||||
@ -72,6 +72,10 @@ enum msgbox_flags {
|
||||
* o Second pointer to the handler function (taking
|
||||
* one (void *), which is incidentally the udata).
|
||||
* o Third any flags.
|
||||
* Each triple should be wrapped in the MSG_BOX_BUTTON
|
||||
* macro, which converts the values to the correct types.
|
||||
* (The compiler can't do that on its own for variadic
|
||||
* arguments.)
|
||||
*
|
||||
* Note that you should ALWAYS format the msg_box() call like:
|
||||
*
|
||||
@ -79,9 +83,9 @@ enum msgbox_flags {
|
||||
* title, align,
|
||||
* text,
|
||||
* udata, M,
|
||||
* label1, handler1, flags1,
|
||||
* MSG_BOX_BUTTON(label1, handler1, flags1),
|
||||
* ...,
|
||||
* labelM, handlerM, flagsM);
|
||||
* MSG_BOX_BUTTON(labelM, handlerM, flagsM));
|
||||
*
|
||||
* ...no matter that it could fit on one line in case of a tiny message box. */
|
||||
struct dialog_data *
|
||||
@ -89,6 +93,21 @@ msg_box(struct terminal *term, struct memory_list *mem_list,
|
||||
enum msgbox_flags flags, unsigned char *title, enum format_align align,
|
||||
unsigned char *text, void *udata, int buttons, ...);
|
||||
|
||||
/* Cast @value to @type and warn if the conversion is suspicious.
|
||||
* If @value has side effects, this does them only once.
|
||||
* The expression used here is intended to be standard C, but it is
|
||||
* somewhat tricky. If it causes trouble on some compiler, you can
|
||||
* #ifdef an alternative definition that skips the type check. */
|
||||
#define MSG_BOX_CAST(type, value) \
|
||||
(((void) sizeof(((int (*)(type)) 0)(value))), (type) (value))
|
||||
|
||||
/* A button in the variadic arguments of msg_box().
|
||||
* This macro expands into three arguments. */
|
||||
#define MSG_BOX_BUTTON(label, handler, flags) \
|
||||
MSG_BOX_CAST(const unsigned char *, label), \
|
||||
MSG_BOX_CAST(done_handler_T *, handler), \
|
||||
MSG_BOX_CAST(int, flags)
|
||||
|
||||
|
||||
/* msg_text() is basically an equivalent to asprintf(), specifically to be used
|
||||
* inside of message boxes. Please always use msg_text() instead of asprintf()
|
||||
|
@ -54,8 +54,8 @@ write_config_dialog(struct terminal *term, unsigned char *config_file,
|
||||
msg_text(term, N_("Options were saved successfully to config file %s."),
|
||||
config_file),
|
||||
NULL, 2,
|
||||
N_("~OK"), NULL, B_ENTER | B_ESC,
|
||||
N_("~Do not show anymore"), disable_success_msgbox, 0);
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC),
|
||||
MSG_BOX_BUTTON(N_("~Do not show anymore"), disable_success_msgbox, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -815,8 +815,8 @@ really_add_keybinding(void *data, unsigned char *keystroke)
|
||||
canonical.length ? canonical.source : keystroke,
|
||||
get_action_name(hop->keymap_id, action_id)),
|
||||
new_hop, 2,
|
||||
N_("~Yes"), really_really_add_keybinding, B_ENTER,
|
||||
N_("~No"), NULL, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), really_really_add_keybinding, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
|
||||
|
||||
done_string(&canonical); /* safe even if init failed */
|
||||
return;
|
||||
|
@ -50,6 +50,18 @@ add_cookie_info_to_string(struct string *string, struct cookie *cookie,
|
||||
_(cookie->secure ? N_("yes") : N_("no"), term));
|
||||
}
|
||||
|
||||
static void
|
||||
accept_cookie_in_msg_box(void *cookie_)
|
||||
{
|
||||
accept_cookie((struct cookie *) cookie_);
|
||||
}
|
||||
|
||||
static void
|
||||
reject_cookie_in_msg_box(void *cookie_)
|
||||
{
|
||||
done_cookie((struct cookie *) cookie_);
|
||||
}
|
||||
|
||||
/* TODO: Store cookie in data arg. --jonas*/
|
||||
void
|
||||
accept_cookie_dialog(struct session *ses, void *data)
|
||||
@ -78,8 +90,8 @@ accept_cookie_dialog(struct session *ses, void *data)
|
||||
N_("Accept cookie?"), ALIGN_LEFT,
|
||||
string.source,
|
||||
cookie, 2,
|
||||
N_("~Accept"), accept_cookie, B_ENTER,
|
||||
N_("~Reject"), done_cookie, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Accept"), accept_cookie_in_msg_box, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~Reject"), reject_cookie_in_msg_box, B_ESC));
|
||||
}
|
||||
|
||||
|
||||
|
@ -131,8 +131,8 @@ menu_keys(struct terminal *term, void *d_, void *xxx)
|
||||
N_("Keys"), ALIGN_LEFT,
|
||||
keys.source,
|
||||
info, 2,
|
||||
N_("~OK"), NULL, B_ENTER | B_ESC,
|
||||
N_("~Toggle display"), push_toggle_keys_display_button, B_ENTER);
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC),
|
||||
MSG_BOX_BUTTON(N_("~Toggle display"), push_toggle_keys_display_button, B_ENTER));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -100,15 +100,19 @@ save_url_as(struct session *ses)
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
really_exit_prog(struct session *ses)
|
||||
static void
|
||||
really_exit_prog(void *ses_)
|
||||
{
|
||||
struct session *ses = ses_;
|
||||
|
||||
register_bottom_half(destroy_terminal, ses->tab->term);
|
||||
}
|
||||
|
||||
static inline void
|
||||
dont_exit_prog(struct session *ses)
|
||||
dont_exit_prog(void *ses_)
|
||||
{
|
||||
struct session *ses = ses_;
|
||||
|
||||
ses->exit_query = 0;
|
||||
}
|
||||
|
||||
@ -124,8 +128,8 @@ query_exit(struct session *ses)
|
||||
"(and terminate all downloads)?")
|
||||
: N_("Do you really want to exit ELinks?"),
|
||||
ses, 2,
|
||||
N_("~Yes"), (void (*)(void *)) really_exit_prog, B_ENTER,
|
||||
N_("~No"), (void (*)(void *)) dont_exit_prog, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), really_exit_prog, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), dont_exit_prog, B_ESC));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -24,7 +24,6 @@ void free_history_lists(void);
|
||||
|
||||
void query_file(struct session *, struct uri *, void *, void (*)(void *, unsigned char *), void (*)(void *), int);
|
||||
|
||||
void really_exit_prog(struct session *ses);
|
||||
void query_exit(struct session *ses);
|
||||
void exit_prog(struct session *ses, int query);
|
||||
|
||||
|
@ -330,20 +330,31 @@ forget_forms_with_url(unsigned char *url)
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Appends form data @form1 (url and submitted_value(s)) to the password file.
|
||||
* Returns 1 on success, 0 otherwise. */
|
||||
static int
|
||||
remember_form(struct formhist_data *form)
|
||||
/* Appends form data @form_ (url and submitted_value(s)) to the password file. */
|
||||
static void
|
||||
remember_form(void *form_)
|
||||
{
|
||||
struct formhist_data *form = form_;
|
||||
|
||||
forget_forms_with_url(form->url);
|
||||
add_to_list(saved_forms, form);
|
||||
|
||||
return save_formhist_to_file();
|
||||
save_formhist_to_file();
|
||||
}
|
||||
|
||||
static int
|
||||
never_for_this_site(struct formhist_data *form)
|
||||
static void
|
||||
dont_remember_form(void *form_)
|
||||
{
|
||||
struct formhist_data *form = form_;
|
||||
|
||||
done_formhist_item(form);
|
||||
}
|
||||
|
||||
static void
|
||||
never_for_this_site(void *form_)
|
||||
{
|
||||
struct formhist_data *form = form_;
|
||||
|
||||
form->dontsave = 1;
|
||||
return remember_form(form);
|
||||
}
|
||||
@ -416,9 +427,9 @@ memorize_form(struct session *ses, struct list_head *submit,
|
||||
"obscured (but unencrypted) in a file on your disk.\n\n"
|
||||
"If you are using a valuable password, answer NO."),
|
||||
form, 3,
|
||||
N_("~Yes"), remember_form, B_ENTER,
|
||||
N_("~No"), done_formhist_item, B_ESC,
|
||||
N_("Ne~ver for this site"), never_for_this_site, NULL);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), remember_form, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), dont_remember_form, B_ESC),
|
||||
MSG_BOX_BUTTON(N_("Ne~ver for this site"), never_for_this_site, 0));
|
||||
|
||||
return;
|
||||
|
||||
|
@ -67,8 +67,8 @@ menu_del_ext(struct terminal *term, void *fcp, void *xxx2)
|
||||
msg_text(term, N_("Delete extension %s -> %s?"),
|
||||
extension, opt->value.string),
|
||||
extension, 2,
|
||||
N_("~Yes"), really_del_ext, B_ENTER,
|
||||
N_("~No"), NULL, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), really_del_ext, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,7 +233,7 @@ generic_external_protocol_handler(struct session *ses, struct uri *uri)
|
||||
"%s protocol support"),
|
||||
protocol_backends[uri->protocol].name),
|
||||
ses, 1,
|
||||
N_("~OK"), NULL, B_ENTER | B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ python_info_box(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
title, ALIGN_LEFT,
|
||||
text,
|
||||
NULL, 1,
|
||||
N_("~OK"), NULL, B_ENTER | B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC));
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
|
@ -493,16 +493,20 @@ struct cdf_hop {
|
||||
};
|
||||
|
||||
static void
|
||||
lun_alternate(struct lun_hop *lun_hop)
|
||||
lun_alternate(void *lun_hop_)
|
||||
{
|
||||
struct lun_hop *lun_hop = lun_hop_;
|
||||
|
||||
lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data, 0);
|
||||
mem_free_if(lun_hop->ofile);
|
||||
mem_free(lun_hop);
|
||||
}
|
||||
|
||||
static void
|
||||
lun_cancel(struct lun_hop *lun_hop)
|
||||
lun_cancel(void *lun_hop_)
|
||||
{
|
||||
struct lun_hop *lun_hop = lun_hop_;
|
||||
|
||||
lun_hop->callback(lun_hop->term, NULL, lun_hop->data, 0);
|
||||
mem_free_if(lun_hop->ofile);
|
||||
mem_free_if(lun_hop->file);
|
||||
@ -510,8 +514,10 @@ lun_cancel(struct lun_hop *lun_hop)
|
||||
}
|
||||
|
||||
static void
|
||||
lun_overwrite(struct lun_hop *lun_hop)
|
||||
lun_overwrite(void *lun_hop_)
|
||||
{
|
||||
struct lun_hop *lun_hop = lun_hop_;
|
||||
|
||||
lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 0);
|
||||
mem_free_if(lun_hop->file);
|
||||
mem_free(lun_hop);
|
||||
@ -520,8 +526,9 @@ lun_overwrite(struct lun_hop *lun_hop)
|
||||
static void common_download_do(struct terminal *term, int fd, void *data, int resume);
|
||||
|
||||
static void
|
||||
lun_resume(struct lun_hop *lun_hop)
|
||||
lun_resume(void *lun_hop_)
|
||||
{
|
||||
struct lun_hop *lun_hop = lun_hop_;
|
||||
struct cdf_hop *cdf_hop = lun_hop->data;
|
||||
|
||||
int magic = *(int *)cdf_hop->data;
|
||||
@ -631,10 +638,10 @@ lookup_unique_name(struct terminal *term, unsigned char *ofile, int resume,
|
||||
empty_string_or_(lun_hop->ofile),
|
||||
empty_string_or_(file)),
|
||||
lun_hop, 4,
|
||||
N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER,
|
||||
N_("~Overwrite the original file"), lun_overwrite, 0,
|
||||
N_("~Resume download of the original file"), lun_resume, 0,
|
||||
N_("~Cancel"), lun_cancel, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~Overwrite the original file"), lun_overwrite, 0),
|
||||
MSG_BOX_BUTTON(N_("~Resume download of the original file"), lun_resume, 0),
|
||||
MSG_BOX_BUTTON(N_("~Cancel"), lun_cancel, B_ESC));
|
||||
}
|
||||
|
||||
|
||||
|
@ -804,7 +804,7 @@ setup_first_session(struct session *ses, struct uri *uri)
|
||||
"Press ESC for menu. Documentation is available in "
|
||||
"Help menu."),
|
||||
ses, 1,
|
||||
N_("~OK"), handler, B_ENTER | B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~OK"), handler, B_ENTER | B_ESC));
|
||||
|
||||
/* If there is no URI the goto dialog will pop up so there is
|
||||
* no need to call setup_session(). */
|
||||
|
@ -85,8 +85,10 @@ ses_load(struct session *ses, struct uri *uri, unsigned char *target_frame,
|
||||
}
|
||||
|
||||
static void
|
||||
post_yes(struct task *task)
|
||||
post_yes(void *task_)
|
||||
{
|
||||
struct task *task = task_;
|
||||
|
||||
abort_preloading(task->ses, 0);
|
||||
|
||||
/* XXX: Make the session inherit the URI. */
|
||||
@ -96,8 +98,10 @@ post_yes(struct task *task)
|
||||
}
|
||||
|
||||
static void
|
||||
post_no(struct task *task)
|
||||
post_no(void *task_)
|
||||
{
|
||||
struct task *task = task_;
|
||||
|
||||
reload(task->ses, CACHE_MODE_NORMAL);
|
||||
done_uri(task->uri);
|
||||
}
|
||||
@ -270,8 +274,8 @@ ses_goto(struct session *ses, struct uri *uri, unsigned char *target_frame,
|
||||
N_("Warning"), ALIGN_CENTER,
|
||||
message,
|
||||
task, 2,
|
||||
N_("~Yes"), post_yes, B_ENTER,
|
||||
N_("~No"), post_no, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), post_yes, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), post_no, B_ESC));
|
||||
}
|
||||
|
||||
|
||||
|
@ -169,8 +169,9 @@ switch_current_tab(struct session *ses, int direction)
|
||||
}
|
||||
|
||||
static void
|
||||
really_close_tab(struct session *ses)
|
||||
really_close_tab(void *ses_)
|
||||
{
|
||||
struct session *ses = ses_;
|
||||
struct terminal *term = ses->tab->term;
|
||||
struct window *current_tab = get_current_tab(term);
|
||||
|
||||
@ -203,13 +204,14 @@ close_tab(struct terminal *term, struct session *ses)
|
||||
N_("Close tab"), ALIGN_CENTER,
|
||||
N_("Do you really want to close the current tab?"),
|
||||
ses, 2,
|
||||
N_("~Yes"), (void (*)(void *)) really_close_tab, B_ENTER,
|
||||
N_("~No"), NULL, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), really_close_tab, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
|
||||
}
|
||||
|
||||
static void
|
||||
really_close_tabs(struct session *ses)
|
||||
really_close_tabs(void *ses_)
|
||||
{
|
||||
struct session *ses = ses_;
|
||||
struct terminal *term = ses->tab->term;
|
||||
struct window *current_tab = get_current_tab(term);
|
||||
struct window *tab;
|
||||
@ -246,8 +248,8 @@ close_all_tabs_but_current(struct session *ses)
|
||||
N_("Close tab"), ALIGN_CENTER,
|
||||
N_("Do you really want to close all except the current tab?"),
|
||||
ses, 2,
|
||||
N_("~Yes"), (void (*)(void *)) really_close_tabs, B_ENTER,
|
||||
N_("~No"), NULL, B_ESC);
|
||||
MSG_BOX_BUTTON(N_("~Yes"), really_close_tabs, B_ENTER),
|
||||
MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user