diff options
Diffstat (limited to 'common/stringx.c')
-rw-r--r-- | common/stringx.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/common/stringx.c b/common/stringx.c new file mode 100644 index 0000000..f0dea56 --- /dev/null +++ b/common/stringx.c @@ -0,0 +1,271 @@ + +#include <sys/types.h> + +#include <syslog.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +#include "usuals.h" +#include "compat.h" +#include "clamsmtpd.h" +#include "util.h" + +/* ---------------------------------------------------------------------------------- + * Logging + */ + +const char kMsgDelimiter[] = ": "; +#define MAX_MSGLEN 256 + +static void vmessage(clamsmtp_context_t* ctx, int level, int err, + const char* msg, va_list ap) +{ + size_t len; + char* m; + int e = errno; + + if(g_daemonized) + { + if(level >= LOG_DEBUG) + return; + } + else + { + if(g_debuglevel < level) + return; + } + + ASSERT(msg); + + len = strlen(msg) + 20 + MAX_MSGLEN; + m = (char*)alloca(len); + + if(m) + { + if(ctx) + snprintf(m, len, "%06X: %s%s", ctx->id, msg, err ? ": " : ""); + else + snprintf(m, len, "%s%s", msg, err ? ": " : ""); + + if(err) + strerror_r(e, m + strlen(m), MAX_MSGLEN); + + m[len - 1] = 0; + msg = m; + } + + /* Either to syslog or stderr */ + if(g_daemonized) + vsyslog(level, msg, ap); + else + vwarnx(msg, ap); +} + +void messagex(clamsmtp_context_t* ctx, int level, const char* msg, ...) +{ + va_list ap; + + va_start(ap, msg); + vmessage(ctx, level, 0, msg, ap); + va_end(ap); +} + +void message(clamsmtp_context_t* ctx, int level, const char* msg, ...) +{ + va_list ap; + + va_start(ap, msg); + vmessage(ctx, level, 1, msg, ap); + va_end(ap); +} + +#define MAX_LOG_LINE 79 + +void log_fd_data(clamsmtp_context_t* ctx, const char* data, int* fd, int read) +{ + #define offsetof(s, m) ((size_t)&(((s*)0)->m)) + #define ismember(o, m) (((char*)(m)) < (((char*)(o)) + sizeof(*(o)))) + #define ptrdiff(o, t) + + char prefix[16]; + const char* t; + + ASSERT(ctx); + ASSERT(ismember(ctx, fd)); + + switch((char*)fd - (char*)ctx) + { + case offsetof(clamsmtp_context_t, client): + strcpy(prefix, "CLIENT "); + break; + case offsetof(clamsmtp_context_t, server): + strcpy(prefix, "SERVER "); + break; + case offsetof(clamsmtp_context_t, clam): + strcpy(prefix, "CLAM "); + break; + default: + strcpy(prefix, "???? "); + break; + } + + strcat(prefix, read ? "< " : "> "); + log_data(ctx, data, prefix); +} + + +void log_data(clamsmtp_context_t* ctx, const char* data, const char* prefix) +{ + char buf[MAX_LOG_LINE + 1]; + int pos, len; + + for(;;) + { + data += strspn(data, "\r\n"); + + if(!*data) + break; + + pos = strcspn(data, "\r\n"); + + len = pos < MAX_LOG_LINE ? pos : MAX_LOG_LINE; + memcpy(buf, data, len); + buf[len] = 0; + + messagex(ctx, LOG_DEBUG, "%s%s", prefix, buf); + + data += pos; + } +} + +/* ---------------------------------------------------------------------------------- + * Parsing + */ + +int is_first_word(const char* line, const char* word, int len) +{ + ASSERT(line); + ASSERT(word); + ASSERT(len > 0); + + while(*line && isspace(*line)) + line++; + + if(strncasecmp(line, word, len) != 0) + return 0; + + line += len; + return !*line || isspace(*line); +} + +int check_first_word(const char* line, const char* word, int len, char* delims) +{ + const char* t; + int found = 0; + + ASSERT(line); + ASSERT(word); + ASSERT(len > 0); + + t = line; + + while(*t && strchr(delims, *t)) + t++; + + if(strncasecmp(t, word, len) != 0) + return 0; + + t += len; + + while(*t && strchr(delims, *t)) + { + found = 1; + t++; + } + + return (!*t || found) ? t - line : 0; +} + +int is_last_word(const char* line, const char* word, int len) +{ + const char* t; + + ASSERT(line); + ASSERT(word); + ASSERT(len > 0); + + t = line + strlen(line); + + while(t > line && isspace(*(t - 1))) + --t; + + if(t - len < line) + return 0; + + return strncasecmp(t - len, word, len) == 0; +} + +int is_blank_line(const char* line) +{ + /* Small optimization */ + if(!*line) + return 1; + + while(*line && isspace(*line)) + line++; + + return *line == 0; +} + +/* ----------------------------------------------------------------------- + * Locking + */ + +void plock() +{ + int r; + +#ifdef _DEBUG + int wait = 0; +#endif + +#ifdef _DEBUG + r = pthread_mutex_trylock(&g_mutex); + if(r == EBUSY) + { + wait = 1; + message(NULL, LOG_DEBUG, "thread will block: %d", pthread_self()); + r = pthread_mutex_lock(&g_mutex); + } + +#else + r = pthread_mutex_lock(&g_mutex); + +#endif + + if(r != 0) + { + errno = r; + message(NULL, LOG_CRIT, "threading problem. couldn't lock mutex"); + } + +#ifdef _DEBUG + else if(wait) + { + message(NULL, LOG_DEBUG, "thread unblocked: %d", pthread_self()); + } +#endif +} + +void punlock() +{ + int r = pthread_mutex_unlock(&g_mutex); + if(r != 0) + { + errno = r; + message(NULL, LOG_CRIT, "threading problem. couldn't unlock mutex"); + } +} + |