Added a bunch of new file natives.
Fixed possible corruption in snprintf when input buffer was bigger than output buffer and relaying on its retval. Fixed all cases when the above situation happened. Fixed _PrintToHL2Log not taking va_list in. --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40279
This commit is contained in:
+13
-9
@@ -35,6 +35,7 @@ void CLogger::_NewMapFile()
|
||||
{
|
||||
g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
|
||||
m_Active = false;
|
||||
return;
|
||||
} else {
|
||||
char date[32];
|
||||
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
|
||||
@@ -125,7 +126,10 @@ void CLogger::LogMessage(const char *vafmt, ...)
|
||||
|
||||
if (m_mode == LoggingMode_HL2)
|
||||
{
|
||||
_PrintToHL2Log(vafmt);
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
_PrintToHL2Log(vafmt, ap);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,7 +139,7 @@ void CLogger::LogMessage(const char *vafmt, ...)
|
||||
_NewMapFile();
|
||||
}
|
||||
|
||||
static char msg[3072];
|
||||
char msg[3072];
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
vsnprintf(msg, sizeof(msg), vafmt, ap);
|
||||
@@ -215,7 +219,7 @@ void CLogger::LogError(const char *vafmt, ...)
|
||||
m_ErrMapStart = false;
|
||||
}
|
||||
|
||||
static char msg[3072];
|
||||
char msg[3072];
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
vsnprintf(msg, sizeof(msg), vafmt, ap);
|
||||
@@ -235,6 +239,7 @@ void CLogger::LogError(const char *vafmt, ...)
|
||||
} else {
|
||||
g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
|
||||
m_Active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
g_SMAPI->ConPrintf("L %s: %s\n", date, msg);
|
||||
@@ -265,17 +270,16 @@ void CLogger::MapChange(const char *mapname)
|
||||
m_ErrMapStart = false;
|
||||
}
|
||||
|
||||
void CLogger::_PrintToHL2Log(const char *fmt, ...)
|
||||
void CLogger::_PrintToHL2Log(const char *fmt, va_list ap)
|
||||
{
|
||||
static char msg[3072];
|
||||
char msg[3072];
|
||||
size_t len;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(msg, sizeof(msg)-1, fmt, ap);
|
||||
len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
|
||||
len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
|
||||
|
||||
msg[len++] = '\n';
|
||||
msg[len] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
engine->LogPrint(msg);
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ public:
|
||||
private:
|
||||
void _CloseFile();
|
||||
void _NewMapFile();
|
||||
void _PrintToHL2Log(const char *fmt, ...);
|
||||
void _PrintToHL2Log(const char *fmt, va_list ap);
|
||||
private:
|
||||
String m_NrmFileName;
|
||||
String m_ErrFileName;
|
||||
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
#include "sm_srvcmds.h"
|
||||
#include "sm_version.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
ConVarAccessor g_ConCmdAccessor;
|
||||
|
||||
@@ -70,15 +71,15 @@ CON_COMMAND(sm, "SourceMod Menu")
|
||||
int len = 0;
|
||||
const sm_plugininfo_t *info = pl->GetPublicInfo();
|
||||
|
||||
len += snprintf(&buffer[len], sizeof(buffer)-len, " %02d <%s>", id, StatusToStr(pl->GetStatus()));
|
||||
len += snprintf(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
|
||||
len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, StatusToStr(pl->GetStatus()));
|
||||
len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
|
||||
if (IS_STR_FILLED(info->version))
|
||||
{
|
||||
len += snprintf(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
|
||||
len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
|
||||
}
|
||||
if (IS_STR_FILLED(info->author))
|
||||
{
|
||||
snprintf(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
|
||||
UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
|
||||
}
|
||||
META_CONPRINTF("%s\n", buffer);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
#define ALT 0x00000001 /* alternate form */
|
||||
@@ -471,3 +472,13 @@ unsigned int strncopy(char *dest, const char *src, size_t count)
|
||||
|
||||
return (dest - start);
|
||||
}
|
||||
|
||||
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
size_t len = vsnprintf(buffer, maxlength, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (len >= maxlength) ? (maxlength - 1) : len;
|
||||
}
|
||||
@@ -10,5 +10,6 @@ using namespace SourcePawn;
|
||||
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param);
|
||||
const char *stristr(const char *str, const char *substr);
|
||||
unsigned int strncopy(char *dest, const char *src, size_t count);
|
||||
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_
|
||||
|
||||
+335
-3
@@ -1,6 +1,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include "sm_globals.h"
|
||||
#include "HandleSys.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
HandleType_t g_FileType;
|
||||
HandleType_t g_DirType;
|
||||
@@ -36,7 +38,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *path;
|
||||
int err;
|
||||
@@ -58,7 +60,7 @@ cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
|
||||
return g_HandleSys.CreateHandle(g_DirType, pDir, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
IDirectory *pDir;
|
||||
@@ -109,6 +111,324 @@ cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
|
||||
return true;
|
||||
}
|
||||
|
||||
static cell_t sm_OpenFile(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name, *mode;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
if ((err=pContext->LocalToString(params[2], &mode)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
|
||||
FILE *pFile = fopen(realpath, mode);
|
||||
if (!pFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_HandleSys.CreateHandle(g_FileType, pFile, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
static cell_t sm_DeleteFile(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
|
||||
return (unlink(realpath)) ? 0 : 1;
|
||||
}
|
||||
|
||||
static cell_t sm_ReadFileLine(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
FILE *pFile;
|
||||
int err;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
char *buf;
|
||||
if ((err=pContext->LocalToString(params[2], &buf)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fgets(buf, params[3], pFile);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_IsEndOfFile(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
FILE *pFile;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return (feof(pFile)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t sm_FileSeek(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
FILE *pFile;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
fseek(pFile, params[2], params[3]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_FilePosition(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
FILE *pFile;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return ftell(pFile);
|
||||
}
|
||||
|
||||
static cell_t sm_FileExists(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
struct _stat s;
|
||||
if (_stat(realpath, &s) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (s.st_mode & S_IFREG)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#elif PLATFORM_LINUX
|
||||
struct stat s;
|
||||
if (stat(realpath, &s) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *newpath, *oldpath;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char new_realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(new_realpath, sizeof(new_realpath), "%s/%s", g_SourceMod.GetBaseDir(), newpath);
|
||||
char old_realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(old_realpath, sizeof(old_realpath), "%s/%s", g_SourceMod.GetBaseDir(), oldpath);
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return (MoveFileA(old_realpath, new_realpath)) ? 1 : 0;
|
||||
#elif PLATFORM_LINUX
|
||||
return (rename(old_realpath, new_realpath)) ? 0 : 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
struct _stat s;
|
||||
if (_stat(realpath, &s) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (s.st_mode & S_IFDIR)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#elif PLATFORM_LINUX
|
||||
struct stat s;
|
||||
if (stat(realpath, &s) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (S_ISDIR(s.st_mode))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static cell_t sm_FileSize(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
struct _stat s;
|
||||
if (_stat(realpath, &s) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (s.st_mode & S_IFREG)
|
||||
{
|
||||
return static_cast<cell_t>(s.st_size);
|
||||
}
|
||||
return -1;
|
||||
#elif PLATFORM_LINUX
|
||||
struct stat s;
|
||||
if (stat(realpath, &s) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
return static_cast<cell_t>(s.st_size);
|
||||
}
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char realpath[PLATFORM_MAX_PATH+1];
|
||||
g_LibSys.PathFormat(realpath, sizeof(realpath), "%s/%s", g_SourceMod.GetBaseDir(), name);
|
||||
|
||||
return (rmdir(realpath)) ? 0 : 1;
|
||||
}
|
||||
|
||||
static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
FILE *pFile;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
char *fmt;
|
||||
int err;
|
||||
if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
int arg = 3;
|
||||
atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
|
||||
fprintf(pFile, "%s\n", buffer);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//:TODO: DEBUG CODE HERE
|
||||
cell_t PrintStuff(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *stuff;
|
||||
@@ -127,6 +447,18 @@ REGISTER_NATIVES(filesystem)
|
||||
{
|
||||
{"OpenDirectory", sm_OpenDirectory},
|
||||
{"ReadDirEntry", sm_ReadDirEntry},
|
||||
{"PrintStuff", PrintStuff},
|
||||
{"PrintStuff", PrintStuff},//:TODO: remove this when no longer needed
|
||||
{"OpenFile", sm_OpenFile},
|
||||
{"DeleteFile", sm_DeleteFile},
|
||||
{"ReadFileLine", sm_ReadFileLine},
|
||||
{"IsEndOfFile", sm_IsEndOfFile},
|
||||
{"FileSeek", sm_FileSeek},
|
||||
{"FilePosition", sm_FilePosition},
|
||||
{"FileExists", sm_FileExists},
|
||||
{"RenameFile", sm_RenameFile},
|
||||
{"DirExists", sm_DirExists},
|
||||
{"FileSize", sm_FileSize},
|
||||
{"RemoveDir", sm_RemoveDir},
|
||||
{"WriteFileLine", sm_WriteFileLine},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
+4
-2
@@ -84,8 +84,9 @@ static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
pCtx->LocalToString(params[2], &str);
|
||||
size_t res = UTIL_Format(str, params[3], "%d", params[1]);
|
||||
|
||||
return snprintf(str, params[3], "%d", params[1]);
|
||||
return static_cast<cell_t>(res);
|
||||
}
|
||||
|
||||
static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
|
||||
@@ -102,8 +103,9 @@ static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
pCtx->LocalToString(params[2], &str);
|
||||
size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
|
||||
|
||||
return snprintf(str, params[3], "%f", sp_ctof(params[1]));
|
||||
return static_cast<cell_t>(res);
|
||||
}
|
||||
|
||||
static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
|
||||
|
||||
@@ -289,6 +289,8 @@ void LibrarySystem::PathFormat(char *buffer, size_t len, const char *fmt, ...)
|
||||
size_t mylen = vsnprintf(buffer, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
mylen = (mylen >= len) ? (len - 1) : mylen;
|
||||
|
||||
for (size_t i=0; i<mylen; i++)
|
||||
{
|
||||
if (buffer[i] == PLATFORM_SEP_ALTCHAR)
|
||||
|
||||
Reference in New Issue
Block a user