Added file dirs/subs to ccenter
This commit is contained in:
parent
ccf25b800f
commit
cf280900be
@ -26,7 +26,7 @@ CC=cc
|
||||
CFLAGS=-I/usr/local/include
|
||||
DEPS = main.c
|
||||
|
||||
OBJ = main.o load_ini.o systemconfig.o systempaths.o mailconfs.o mailareas.o ../../inih/ini.o
|
||||
OBJ = main.o load_ini.o systemconfig.o systempaths.o mailconfs.o mailareas.o filedirs.o filesubs.o ../../inih/ini.o
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $< $(CFLAGS) -I../../deps/cdk-5.0-20161210/include/
|
||||
|
||||
|
@ -176,4 +176,6 @@ extern void system_config(void);
|
||||
extern void system_paths();
|
||||
extern void mail_conferences();
|
||||
extern void edit_mail_areas(int confer);
|
||||
extern void file_directories();
|
||||
extern void edit_file_subdirs(int fdir);
|
||||
#endif
|
||||
|
206
utils/ccenter/filedirs.c
Normal file
206
utils/ccenter/filedirs.c
Normal file
@ -0,0 +1,206 @@
|
||||
#include <curses.h>
|
||||
#include <cdk.h>
|
||||
#include "ccenter.h"
|
||||
|
||||
extern struct bbs_config conf;
|
||||
extern CDKSCREEN *cdkscreen;
|
||||
|
||||
void edit_file_directory_name(int fdir) {
|
||||
char *entrytext;
|
||||
CDKENTRY *dirName = newCDKEntry(cdkscreen, 7, 7, "</B/32>File Directory Name<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 64, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(dirName, conf.file_directories[fdir]->name, 1, 64, TRUE);
|
||||
|
||||
entrytext = activateCDKEntry(dirName, 0);
|
||||
|
||||
if (dirName->exitType == vNORMAL) {
|
||||
if (conf.file_directories[fdir]->name != NULL) {
|
||||
free(conf.file_directories[fdir]->name);
|
||||
}
|
||||
conf.file_directories[fdir]->name = strdup(entrytext);
|
||||
}
|
||||
destroyCDKEntry(dirName);
|
||||
}
|
||||
|
||||
void edit_file_directory_file(int fdir) {
|
||||
char *entrytext;
|
||||
CDKENTRY *dirFile = newCDKEntry(cdkscreen, 7, 7, "</B/32>File Dir. INI Path & Filename<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 1024, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(dirFile, conf.file_directories[fdir]->path, 1, 1024, TRUE);
|
||||
|
||||
entrytext = activateCDKEntry(dirFile, 0);
|
||||
|
||||
if (dirFile->exitType == vNORMAL) {
|
||||
if (conf.file_directories[fdir]->path != NULL) {
|
||||
free(conf.file_directories[fdir]->path);
|
||||
}
|
||||
conf.file_directories[fdir]->path = strdup(entrytext);
|
||||
}
|
||||
destroyCDKEntry(dirFile);
|
||||
}
|
||||
|
||||
void display_fdir_error() {
|
||||
char *message[] = {"Must have at least one File Directory!"};
|
||||
char *buttons[] = {" OK "};
|
||||
CDKDIALOG *dialog = newCDKDialog(cdkscreen, 7, 7, message, 1, buttons, 1, A_STANDOUT, FALSE, TRUE, FALSE);
|
||||
activateCDKDialog(dialog, 0);
|
||||
destroyCDKDialog(dialog);
|
||||
}
|
||||
|
||||
|
||||
void delete_file_directory(int fdir) {
|
||||
char *message[] = {"Do you want to Delete Related Files?"};
|
||||
char *buttons[] = {" Yes ", " No "};
|
||||
char filename[PATH_MAX];
|
||||
int area;
|
||||
|
||||
if (conf.file_directory_count == 1) {
|
||||
display_fdir_error();
|
||||
return;
|
||||
}
|
||||
|
||||
CDKDIALOG *dialog = newCDKDialog(cdkscreen, 7, 7, message, 1, buttons, 2, A_STANDOUT, FALSE, TRUE, FALSE);
|
||||
|
||||
int selection = activateCDKDialog(dialog, 0);
|
||||
int i;
|
||||
|
||||
if (dialog->exitType == vESCAPE_HIT || (dialog->exitType == vNORMAL && selection == 1)) {
|
||||
// do nothing
|
||||
} else {
|
||||
for (area=0;area<conf.file_directories[fdir]->file_sub_count;area++) {
|
||||
snprintf(filename, PATH_MAX, "%s.sq3", conf.file_directories[fdir]->file_subs[area]->database);
|
||||
unlink(filename);
|
||||
}
|
||||
unlink(conf.file_directories[fdir]->path);
|
||||
}
|
||||
destroyCDKDialog(dialog);
|
||||
|
||||
for (area=0;area<conf.file_directories[fdir]->file_sub_count;area++) {
|
||||
free(conf.file_directories[fdir]->file_subs[area]->name);
|
||||
free(conf.file_directories[fdir]->file_subs[area]->database);
|
||||
free(conf.file_directories[fdir]->file_subs[area]->upload_path);
|
||||
free(conf.file_directories[fdir]->file_subs[area]);
|
||||
|
||||
}
|
||||
|
||||
free(conf.file_directories[fdir]->file_subs);
|
||||
free(conf.file_directories[fdir]->name);
|
||||
free(conf.file_directories[fdir]->path);
|
||||
|
||||
for (i=fdir;i<conf.file_directory_count - 1;i++) {
|
||||
conf.file_directories[i]= conf.file_directories[i+1];
|
||||
}
|
||||
|
||||
conf.file_directory_count--;
|
||||
conf.file_directories = (struct file_directory **)realloc(conf.file_directories, sizeof(struct file_directory *) * conf.file_directory_count);
|
||||
}
|
||||
|
||||
void edit_file_directory(int fdir) {
|
||||
char *itemList[] = {"Name",
|
||||
"Path & Filename",
|
||||
"Visible Sec. Level",
|
||||
"Edit Sub Directories",
|
||||
"Delete Directory"};
|
||||
|
||||
char buffer[1024];
|
||||
int selection;
|
||||
|
||||
snprintf(buffer, 1024, "</B/32>%s<!32>", conf.file_directories[fdir]->name);
|
||||
|
||||
CDKSCROLL *editDirList = newCDKScroll(cdkscreen, 5, 5, RIGHT, 8, 30, buffer, itemList, 5, FALSE, A_STANDOUT, TRUE, FALSE);
|
||||
|
||||
while (1) {
|
||||
selection = activateCDKScroll(editDirList, 0);
|
||||
if (editDirList->exitType == vESCAPE_HIT) {
|
||||
break;
|
||||
}
|
||||
switch(selection) {
|
||||
case 0:
|
||||
edit_file_directory_name(fdir);
|
||||
break;
|
||||
case 1:
|
||||
edit_file_directory_file(fdir);
|
||||
break;
|
||||
case 2:
|
||||
choose_sec_level(&conf.file_directories[fdir]->sec_level, 7, 7, conf.file_directories[fdir]->sec_level);
|
||||
break;
|
||||
case 3:
|
||||
edit_file_subdirs(fdir);
|
||||
break;
|
||||
case 4:
|
||||
delete_file_directory(fdir);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
destroyCDKScroll(editDirList);
|
||||
}
|
||||
|
||||
void add_new_file_directory() {
|
||||
char areapath[PATH_MAX];
|
||||
conf.file_directories = (struct file_directory **)realloc(conf.file_directories, sizeof(struct file_directory *) * (conf.file_directory_count + 1));
|
||||
conf.file_directories[conf.file_directory_count] = (struct file_directory *)malloc(sizeof(struct file_directory));
|
||||
conf.file_directories[conf.file_directory_count]->file_subs = (struct file_sub **)malloc(sizeof(struct file_sub *));
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0] = (struct file_sub *)malloc(sizeof(struct file_sub));
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0]->name = strdup("New Sub Directory");
|
||||
snprintf(areapath, PATH_MAX, "%s/new_area", conf.bbs_path);
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0]->database = strdup(areapath);
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0]->upload_path = strdup(conf.bbs_path);
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0]->upload_sec_level = conf.newuserlvl;
|
||||
conf.file_directories[conf.file_directory_count]->file_subs[0]->download_sec_level = conf.newuserlvl;
|
||||
conf.file_directories[conf.file_directory_count]->file_sub_count = 1;
|
||||
|
||||
conf.file_directories[conf.file_directory_count]->name = strdup("New Directory");
|
||||
snprintf(areapath, PATH_MAX, "%s/new_dir.ini", conf.config_path);
|
||||
conf.file_directories[conf.file_directory_count]->path = strdup(areapath);
|
||||
conf.file_directories[conf.file_directory_count]->sec_level = conf.newuserlvl;
|
||||
|
||||
conf.file_directory_count ++;
|
||||
|
||||
edit_file_directory(conf.file_directory_count-1);
|
||||
}
|
||||
|
||||
|
||||
void file_directories() {
|
||||
char **itemlist;
|
||||
int selection;
|
||||
int i;
|
||||
int dircount;
|
||||
|
||||
while (1) {
|
||||
itemlist = (char **)malloc(sizeof(char *) * (conf.file_directory_count + 1));
|
||||
dircount = conf.file_directory_count;
|
||||
itemlist[0] = strdup("Add File Directory");
|
||||
|
||||
for (i=0;i<dircount;i++) {
|
||||
itemlist[i+1] = strdup(conf.file_directories[i]->name);
|
||||
}
|
||||
|
||||
CDKSCROLL *fileDirList = newCDKScroll(cdkscreen, 3, 3, RIGHT, 12, 30, "</B/32>File Directories<!32>", itemlist, dircount + 1, FALSE, A_STANDOUT, TRUE, FALSE);
|
||||
|
||||
|
||||
selection = activateCDKScroll(fileDirList, 0);
|
||||
if (fileDirList->exitType == vESCAPE_HIT) {
|
||||
destroyCDKScroll(fileDirList);
|
||||
for (i=0;i<dircount+1;i++) {
|
||||
free(itemlist[i]);
|
||||
}
|
||||
free(itemlist);
|
||||
break;
|
||||
}
|
||||
if (selection == 0) {
|
||||
// add new
|
||||
add_new_file_directory();
|
||||
} else {
|
||||
// edit existing
|
||||
edit_file_directory(selection - 1);
|
||||
}
|
||||
destroyCDKScroll(fileDirList);
|
||||
for (i=0;i<dircount+1;i++) {
|
||||
free(itemlist[i]);
|
||||
}
|
||||
free(itemlist);
|
||||
}
|
||||
}
|
204
utils/ccenter/filesubs.c
Normal file
204
utils/ccenter/filesubs.c
Normal file
@ -0,0 +1,204 @@
|
||||
#include <curses.h>
|
||||
#include <cdk.h>
|
||||
#include "ccenter.h"
|
||||
|
||||
extern struct bbs_config conf;
|
||||
extern CDKSCREEN *cdkscreen;
|
||||
|
||||
void edit_file_sub_name(int fdir, int area) {
|
||||
char *entrytext;
|
||||
CDKENTRY *areaName = newCDKEntry(cdkscreen, 11, 11, "</B/32>File Sub Name<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 64, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(areaName, conf.file_directories[fdir]->file_subs[area]->name, 1, 64, TRUE);
|
||||
|
||||
entrytext = activateCDKEntry(areaName, 0);
|
||||
|
||||
if (areaName->exitType == vNORMAL) {
|
||||
if (conf.file_directories[fdir]->file_subs[area]->name != NULL) {
|
||||
free(conf.file_directories[fdir]->file_subs[area]->name );
|
||||
}
|
||||
conf.file_directories[fdir]->file_subs[area]->name = strdup(entrytext);
|
||||
}
|
||||
destroyCDKEntry(areaName);
|
||||
}
|
||||
|
||||
void edit_file_sub_dbase(int fdir, int area) {
|
||||
char *entrytext;
|
||||
CDKENTRY *areaFile = newCDKEntry(cdkscreen, 11, 11, "</B/32>Database Path & Filename<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 1024, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(areaFile, conf.file_directories[fdir]->file_subs[area]->database, 1, 1024, TRUE);
|
||||
|
||||
entrytext = activateCDKEntry(areaFile, 0);
|
||||
|
||||
if (areaFile->exitType == vNORMAL) {
|
||||
if (conf.file_directories[fdir]->file_subs[area]->database != NULL) {
|
||||
free(conf.file_directories[fdir]->file_subs[area]->database);
|
||||
}
|
||||
conf.file_directories[fdir]->file_subs[area]->database = strdup(entrytext);
|
||||
}
|
||||
destroyCDKEntry(areaFile);
|
||||
}
|
||||
|
||||
|
||||
void edit_file_sub_uploadp(int fdir, int area) {
|
||||
char *entrytext;
|
||||
CDKENTRY *areaFile = newCDKEntry(cdkscreen, 11, 11, "</B/32>Upload Path<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 1024, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(areaFile, conf.file_directories[fdir]->file_subs[area]->upload_path, 1, 1024, TRUE);
|
||||
|
||||
entrytext = activateCDKEntry(areaFile, 0);
|
||||
|
||||
if (areaFile->exitType == vNORMAL) {
|
||||
if (conf.file_directories[fdir]->file_subs[area]->upload_path != NULL) {
|
||||
free(conf.file_directories[fdir]->file_subs[area]->upload_path);
|
||||
}
|
||||
conf.file_directories[fdir]->file_subs[area]->upload_path = strdup(entrytext);
|
||||
}
|
||||
destroyCDKEntry(areaFile);
|
||||
}
|
||||
|
||||
void display_fsub_error() {
|
||||
char *message[] = {"A directory must have at least one sub directory!"};
|
||||
char *buttons[] = {" OK "};
|
||||
CDKDIALOG *dialog = newCDKDialog(cdkscreen, 11, 11, message, 1, buttons, 1, A_STANDOUT, FALSE, TRUE, FALSE);
|
||||
activateCDKDialog(dialog, 0);
|
||||
destroyCDKDialog(dialog);
|
||||
}
|
||||
|
||||
void delete_file_sub(int fdir, int area) {
|
||||
char *message[] = {"Do you want to Delete Related Files?"};
|
||||
char *buttons[] = {" Yes ", " No "};
|
||||
char filename[PATH_MAX];
|
||||
|
||||
if (conf.file_directories[fdir]->file_sub_count == 1) {
|
||||
display_fsub_error();
|
||||
return;
|
||||
}
|
||||
|
||||
CDKDIALOG *dialog = newCDKDialog(cdkscreen, 11, 11, message, 1, buttons, 2, A_STANDOUT, FALSE, TRUE, FALSE);
|
||||
|
||||
int selection = activateCDKDialog(dialog, 0);
|
||||
int i;
|
||||
|
||||
if (dialog->exitType == vESCAPE_HIT || (dialog->exitType == vNORMAL && selection == 1)) {
|
||||
// do nothing
|
||||
} else {
|
||||
snprintf(filename, PATH_MAX, "%s.sq3", conf.file_directories[fdir]->file_subs[area]->database);
|
||||
unlink(filename);
|
||||
}
|
||||
destroyCDKDialog(dialog);
|
||||
|
||||
free(conf.file_directories[fdir]->file_subs[area]->name);
|
||||
free(conf.file_directories[fdir]->file_subs[area]->database);
|
||||
free(conf.file_directories[fdir]->file_subs[area]->upload_path);
|
||||
free(conf.file_directories[fdir]->file_subs[area]);
|
||||
|
||||
for (i=area;i<conf.file_directories[fdir]->file_sub_count - 1;i++) {
|
||||
conf.file_directories[fdir]->file_subs[i] = conf.file_directories[fdir]->file_subs[i+1];
|
||||
}
|
||||
conf.file_directories[fdir]->file_sub_count--;
|
||||
conf.file_directories[fdir]->file_subs = (struct file_sub **)realloc(conf.file_directories[fdir]->file_subs, sizeof(struct file_sub *) * conf.file_directories[fdir]->file_sub_count);
|
||||
}
|
||||
|
||||
void edit_file_sub(int fdir, int area) {
|
||||
char *itemList[] = {"Name",
|
||||
"Database Dir & Path",
|
||||
"Upload Path",
|
||||
"Upload Sec. Level",
|
||||
"Download Sec. Level",
|
||||
"Delete File Sub Dir"};
|
||||
|
||||
char buffer[1024];
|
||||
int selection;
|
||||
|
||||
snprintf(buffer, 1024, "</B/32>%s<!32>", conf.file_directories[fdir]->file_subs[area]->name);
|
||||
|
||||
CDKSCROLL *editSubList = newCDKScroll(cdkscreen, 9, 9, RIGHT, 9, 30, buffer, itemList, 6, FALSE, A_STANDOUT, TRUE, FALSE);
|
||||
|
||||
while (1) {
|
||||
selection = activateCDKScroll(editSubList, 0);
|
||||
if (editSubList->exitType == vESCAPE_HIT) {
|
||||
break;
|
||||
}
|
||||
switch(selection) {
|
||||
case 0:
|
||||
edit_file_sub_name(fdir, area);
|
||||
break;
|
||||
case 1:
|
||||
edit_file_sub_dbase(fdir, area);
|
||||
break;
|
||||
case 2:
|
||||
edit_file_sub_uploadp(fdir, area);
|
||||
break;
|
||||
case 3:
|
||||
choose_sec_level(&conf.file_directories[fdir]->file_subs[area]->upload_sec_level, 11, 11, conf.file_directories[fdir]->file_subs[area]->upload_sec_level);
|
||||
break;
|
||||
case 4:
|
||||
choose_sec_level(&conf.file_directories[fdir]->file_subs[area]->download_sec_level, 11, 11, conf.file_directories[fdir]->file_subs[area]->download_sec_level);
|
||||
break;
|
||||
case 5:
|
||||
delete_file_sub(fdir, area);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
destroyCDKScroll(editSubList);
|
||||
}
|
||||
|
||||
void add_new_file_sub(int fdir) {
|
||||
char areapath[PATH_MAX];
|
||||
conf.file_directories[fdir]->file_subs = (struct file_sub **)realloc(conf.file_directories[fdir]->file_subs, sizeof(struct file_sub *) * (conf.file_directories[fdir]->file_sub_count + 1));
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count] = (struct file_sub *)malloc(sizeof(struct file_sub));
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count]->name = strdup("New Sub Directory");
|
||||
snprintf(areapath, PATH_MAX, "%s/new_area", conf.bbs_path);
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count]->database = strdup(areapath);
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count]->upload_path = strdup(conf.bbs_path);
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count]->upload_sec_level = conf.newuserlvl;
|
||||
conf.file_directories[fdir]->file_subs[conf.file_directories[fdir]->file_sub_count]->download_sec_level = conf.newuserlvl;
|
||||
conf.file_directories[fdir]->file_sub_count++;
|
||||
edit_file_sub(fdir, conf.file_directories[fdir]->file_sub_count - 1);
|
||||
}
|
||||
|
||||
void edit_file_subdirs(int fdir) {
|
||||
char **itemlist;
|
||||
int selection;
|
||||
int i;
|
||||
int area_count;
|
||||
|
||||
while (1) {
|
||||
itemlist = (char **)malloc(sizeof(char *) * (conf.file_directories[fdir]->file_sub_count + 1));
|
||||
|
||||
itemlist[0] = strdup("Add Sub Directory");
|
||||
area_count = conf.file_directories[fdir]->file_sub_count;
|
||||
for (i=0;i<area_count;i++) {
|
||||
itemlist[i+1] = strdup(conf.file_directories[fdir]->file_subs[i]->name);
|
||||
}
|
||||
|
||||
CDKSCROLL *fileSubList = newCDKScroll(cdkscreen, 7, 7, RIGHT, 12, 30, "</B/32>File Sub Dirs<!32>", itemlist, area_count + 1, FALSE, A_STANDOUT, TRUE, FALSE);
|
||||
|
||||
|
||||
selection = activateCDKScroll(fileSubList, 0);
|
||||
if (fileSubList->exitType == vESCAPE_HIT) {
|
||||
destroyCDKScroll(fileSubList);
|
||||
for (i=0;i<area_count+1;i++) {
|
||||
free(itemlist[i]);
|
||||
}
|
||||
free(itemlist);
|
||||
break;
|
||||
}
|
||||
if (selection == 0) {
|
||||
// add new
|
||||
add_new_file_sub(fdir);
|
||||
} else {
|
||||
// edit existing
|
||||
edit_file_sub(fdir, selection - 1);
|
||||
}
|
||||
destroyCDKScroll(fileSubList);
|
||||
for (i=0;i<area_count+1;i++) {
|
||||
free(itemlist[i]);
|
||||
}
|
||||
free(itemlist);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ void edit_mail_conference_name(int confer) {
|
||||
|
||||
void edit_mail_conference_file(int confer) {
|
||||
char *entrytext;
|
||||
CDKENTRY *confFile = newCDKEntry(cdkscreen, 7, 7, "</B/32>Conference Path & Filename<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 1024, TRUE, FALSE);
|
||||
CDKENTRY *confFile = newCDKEntry(cdkscreen, 7, 7, "</B/32>Conference INI Path & Filename<!32>", NULL, A_NORMAL, ' ', vMIXED, 48, 1, 1024, TRUE, FALSE);
|
||||
|
||||
setCDKEntry(confFile, conf.mail_conferences[confer]->path, 1, 1024, TRUE);
|
||||
|
||||
@ -249,7 +249,6 @@ void delete_mail_conference(int confer) {
|
||||
free(conf.mail_conferences[confer]->mail_areas[area]->path);
|
||||
free(conf.mail_conferences[confer]->mail_areas[area]->qwkname);
|
||||
free(conf.mail_conferences[confer]->mail_areas[area]);
|
||||
free(conf.mail_conferences[confer]->mail_areas);
|
||||
}
|
||||
|
||||
free(conf.mail_conferences[confer]->mail_areas);
|
||||
@ -270,7 +269,7 @@ void delete_mail_conference(int confer) {
|
||||
|
||||
void edit_mail_conference(int confer) {
|
||||
char *itemList[] = {"Name",
|
||||
"Path & Filename",
|
||||
"INI Path & Filename",
|
||||
"Visible Sec. Level",
|
||||
"Networked",
|
||||
"Tag Line",
|
||||
@ -384,7 +383,8 @@ void mail_conferences() {
|
||||
}
|
||||
if (selection == 0) {
|
||||
// add new
|
||||
add_new_mail_conference(); } else {
|
||||
add_new_mail_conference();
|
||||
} else {
|
||||
// edit existing
|
||||
edit_mail_conference(selection - 1);
|
||||
}
|
||||
|
@ -90,6 +90,9 @@ int main(int argc, char **argv) {
|
||||
case 2:
|
||||
mail_conferences();
|
||||
break;
|
||||
case 3:
|
||||
file_directories();
|
||||
break;
|
||||
case 5:
|
||||
doexit = 1;
|
||||
break;
|
||||
|
Reference in New Issue
Block a user