summaryrefslogtreecommitdiff
path: root/common/stringx.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/stringx.c')
-rw-r--r--common/stringx.c271
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");
+ }
+}
+