]> source.dussan.org Git - rspamd.git/commitdiff
* Rewrite perl client for rspamd, now it allows access to both normal and control...
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Mon, 19 Jan 2009 14:01:08 +0000 (17:01 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Mon, 19 Jan 2009 14:01:08 +0000 (17:01 +0300)
* Fix small errors in tokenizer and controller interface

rspamc.pl
src/controller.c
src/fstring.c
src/fstring.h
src/statfile.c
src/tokenizers/osb.c
src/tokenizers/tokenizers.c

index ee203b85ad4211cdbd378d6663d905d912f1b81d..46940987cae6c441a32103586c0e400cc8ff8ec2 100755 (executable)
--- a/rspamc.pl
+++ b/rspamc.pl
@@ -3,7 +3,7 @@
 # Simple script that read message from STDIN and test it on rspamd server
 # using specified command.
 #
-# Usage: rspamc.pl [-c conf_file] [command]
+# Usage: rspamc.pl [-c conf_file] [command] [-s statfile]
 #
 # By default rspamc.pl would read ./rspamd.conf and default command is SYMBOLS
 
@@ -14,95 +14,215 @@ my %cfg = (
     'command'   => 'SYMBOLS',
     'host'      => 'localhost',
     'port'      => '11333',
-    'is_unix'   => 0,
+    'is_unix'   =>  0,
+    'password'  =>  '',
+    'control'   =>  0,
+    'statfile'  =>  '',
 );
 
+
 sub usage {
-    return "Usage: rspamc.pl [-c conf_file] [command]";
+    return "Usage: rspamc.pl [-c conf_file] [-s statfile] [command]";
 }
 
-while (my $param = shift) {
-    if ($param eq '-c') {
-        my $value = shift;
-        if ($value) {
-            if (-r $value) {
-                $cfg{'conf_file'} = $value;
+# Load rspamd config params
+sub parse_config {
+    my ($is_ctrl) = @_;
+
+    open CONF, "< $cfg{'conf_file'}" or die "config file $cfg{'conf_file'} cannot be opened";
+
+    my $ctrl = 0;
+    while (<CONF>) {
+        if ($_ =~ /control\s*{/i) {
+            $ctrl = 1;
+        }
+        if ($ctrl && $_ =~ /}/) {
+            $ctrl = 0;
+        }
+        if (((!$is_ctrl && !$ctrl) || ($ctrl && $is_ctrl))
+                && $_ =~ /^\s*bind_socket\s*=\s*((([^:]+):(\d+))|(\/\S*))/i) {
+            if ($3 && $4) {
+                $cfg{'host'} = $3;
+                $cfg{'port'} = $4;
+                $cfg{'is_unix'} = 0;
             }
             else {
-                die "config file $value is not readable";
+                $cfg{'host'} = $5;
+                $cfg{'is_unix'} = 1;
             }
         }
-        else {
-            die usage();
+        if ($ctrl && $is_ctrl && $_ =~ /^\s*password\s*=\s*"(\S+)"/) {
+            $cfg{'password'} = $1;
         }
     }
-    elsif ($param =~ /(SYMBOLS|SCAN|PROCESS|CHECK|REPORT_IFSPAM|REPORT)/i) {
-        $cfg{'command'} = $1;
-    }
+
+    close CONF;
+
 }
 
-open CONF, "< $cfg{'conf_file'}" or die "config file $cfg{'conf_file'} cannot be opened";
+sub connect_socket {
+    my $sock;
 
-my $ctrl = 0;
-while (<CONF>) {
-    if ($_ =~ /control\s*{/i) {
-        $ctrl = 1;
-    }
-    if ($ctrl && $_ =~ /}/) {
-        $ctrl = 0;
+    if ($cfg{'is_unix'}) {
+        socket ($sock, PF_UNIX, SOCK_STREAM, 0) or die "cannot create unix socket";
+        my $sun = sockaddr_un($cfg{'host'});
+        connect ($sock, $sun) or die "cannot connect to socket $cfg{'host'}";
     }
-    if (!$ctrl && $_ =~ /^\s*bind_socket\s*=\s*((([^:]+):(\d+))|(\/\S*))/i) {
-        if ($3 && $4) {
-            $cfg{'host'} = $3;
-            $cfg{'port'} = $4;
-            $cfg{'is_unix'} = 0;
+    else {
+        my $proto = getprotobyname('tcp');
+        my $sin;
+        socket ($sock, PF_INET, SOCK_STREAM, $proto) or die "cannot create tcp socket";
+        if (inet_aton ($cfg{'host'})) {
+            $sin = sockaddr_in ($cfg{'port'}, inet_aton($cfg{'host'}));
         }
         else {
-            $cfg{'host'} = $5;
-            $cfg{'is_unix'} = 1;
+            my $addr = gethostbyname($cfg{'host'});
+            if (!$addr) {
+                die "cannot resolve $cfg{'host'}";
+            }
+            $sin = sockaddr_in ($cfg{'port'}, $addr);
         }
-        last;
+        
+        connect ($sock, $sin) or die "cannot connect to socket $cfg{'host'}:$cfg{'port'}";
     }
+    return $sock;
 }
 
-close CONF;
+# Currently just read stdin for user's message and pass it to rspamd
+sub do_rspamc_command {
+    my ($sock) = @_;
+
+    my $input;
+    while (defined (my $line = <>)) {
+        $input .= $line;
+    }
+
+    print "Sending ". length ($input) ." bytes...\n";
 
-if ($cfg{'is_unix'}) {
-    my $proto = getprotobyname('tcp');
-    socket (SOCK, PF_UNIX, SOCK_STREAM, $proto) or die "cannot create unix socket";
-    my $sun = sockaddr_un($cfg{'host'});
-    connect (SOCK, $sun) or die "cannot connect to socket $cfg{'host'}";
+    syswrite $sock, "$cfg{'command'} RSPAMC/1.0 $CRLF";
+    syswrite $sock, "Content-Length: " . length ($input) . $CRLF . $CRLF;
+    syswrite $sock, $input;
+    syswrite $sock, $CRLF;
+    while (<$sock>) {
+        print $_;
+    }
 }
-else {
-    my $proto = getprotobyname('tcp');
-    my $sin;
-    socket (SOCK, PF_INET, SOCK_STREAM, $proto) or die "cannot create tcp socket";
-    if (inet_aton ($cfg{'host'})) {
-        $sin = sockaddr_in ($cfg{'port'}, inet_aton($cfg{'host'}));
+
+sub do_ctrl_auth {
+    my ($sock) = @_;
+
+    syswrite $sock, "password $cfg{'password'}" . $CRLF;
+    if (defined (my $reply = <$sock>)) {
+        my $end = <$sock>;
+        if ($reply =~ /^password accepted/) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+sub do_control_command {
+    my ($sock) = @_;
+
+    # Read greeting first
+    if (defined (my $greeting = <$sock>)) {
+        if ($greeting !~ /^Rspamd version/) {
+            die "not rspamd greeting line $greeting";
+        }
+    }
+    if ($cfg{'command'} =~ /^learn$/i) {
+        my $input;
+        die "statfile is not specified to learn command" if !$cfg{'statfile'};
+
+        while (defined (my $line = <>)) {
+            $input .= $line;
+        }
+        
+        if (do_ctrl_auth ($sock)) {
+            my $len = length ($input);
+            print "Sending $len bytes...\n";
+            syswrite $sock, "learn $cfg{'statfile'} $len" . $CRLF;
+            syswrite $sock, $input . $CRLF;
+            if (defined (my $reply = <$sock>)) {
+                if ($reply =~ /^learn ok/) {
+                    print "Learn succeed\n";
+                }
+                else {
+                    print "Learn failed\n";
+                }
+            }
+        }
+        else {
+            print "Authentication failed\n";
+        }
+    }
+    elsif ($cfg{'command'} =~ /(reload|shutdown)/i) {
+        if (do_ctrl_auth ($sock)) {
+            syswrite $sock, $cfg{'command'} . $CRLF;
+            while (defined (my $line = <$sock>)) {
+                last if $line =~ /^END/;
+                print $line;
+            }
+        }
+        else {
+            print "Authentication failed\n";
+        }
     }
     else {
-        my $addr = gethostbyname($cfg{'host'});
-        if (!$addr) {
-            die "cannot resolve $cfg{'host'}";
+        syswrite $sock, $cfg{'command'} . $CRLF;
+        while (defined (my $line = <$sock>)) {
+            last if $line =~ /^END/;
+            print $line;
         }
-        $sin = sockaddr_in ($cfg{'port'}, $addr);
     }
-    
-    connect (SOCK, $sin) or die "cannot connect to socket $cfg{'host'}:$cfg{'port'}";
 }
 
-my $input;
-while (defined (my $line = <>)) {
-    $input .= $line;
+while (my $param = shift) {
+    if ($param eq '-c') {
+        my $value = shift;
+        if ($value) {
+            if (-r $value) {
+                $cfg{'conf_file'} = $value;
+            }
+            else {
+                die "config file $value is not readable";
+            }
+        }
+        else {
+            die usage();
+        }
+    }
+    elsif ($param eq '-s') {
+        my $value = shift;
+        if ($value) {
+            $cfg{'statfile'} = $value;
+        }
+        else {
+            die usage();
+        }
+    }
+    elsif ($param =~ /(SYMBOLS|SCAN|PROCESS|CHECK|REPORT_IFSPAM|REPORT)/i) {
+        $cfg{'command'} = $1;
+        $cfg{'control'} = 0;
+    }
+    elsif ($param =~ /(STAT|LEARN|SHUTDOWN|RELOAD|UPTIME)/i) {
+        $cfg{'command'} = $1;
+        $cfg{'control'} = 1;
+    }
+    else {
+        die usage();
+    }
 }
 
-print "Sending ". length ($input) ." bytes...\n";
+parse_config ($cfg{'control'});
+my $sock = connect_socket ();
 
-syswrite SOCK, "$cfg{'command'} RSPAMC/1.0 $CRLF";
-syswrite SOCK, "Content-Length: " . length ($input) . $CRLF . $CRLF;
-syswrite SOCK, $input;
-syswrite SOCK, $CRLF;
-while (<SOCK>) {
-    print $_;
+if ($cfg{'control'}) {
+    do_control_command ($sock);
 }
-close SOCK;
+else {
+    do_rspamc_command ($sock);
+}
+
+close ($sock);
index fb14cf8bbfe33efa397afbf43cd01d722da4ef16..56eeb042af843419b624d3550cef94e3bb8530c9 100644 (file)
@@ -26,6 +26,7 @@
 #include "classifiers/classifiers.h"
 
 #define CRLF "\r\n"
+#define END "END" CRLF
 
 enum command_type {
        COMMAND_PASSWORD,
@@ -149,9 +150,7 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
                        }
                        break;
                case COMMAND_QUIT:
-                               session->state = STATE_QUIT;
-                               r = snprintf (out_buf, sizeof (out_buf), "bye" CRLF);
-                               bufferevent_write (session->bev, out_buf, r);
+                       session->state = STATE_QUIT;
                        break;
                case COMMAND_RELOAD:
                        if (check_auth (cmd, session)) {
@@ -170,7 +169,6 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
                                                          session->worker->srv->stat->connections_count);
                                r += snprintf (out_buf + r, sizeof (out_buf) - r, "Control connections count: %u" CRLF,
                                                          session->worker->srv->stat->control_connections_count);
-                               r += snprintf (out_buf + r, sizeof (out_buf) - r, "-- end of stats report" CRLF);
                                bufferevent_write (session->bev, out_buf, r);
                        }
                        break;
@@ -187,8 +185,8 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
                                /* If uptime more than 2 hours, print as a number of days. */
                                if (uptime >= 2 * 3600) {
                                        days = uptime / 86400;
-                                       hours = (uptime % 3600) / 60;
-                                       minutes = (uptime % 60) / 60;
+                                       hours = uptime / 3600 - days * 86400;
+                                       minutes = uptime / 60 - hours * 3600 - days * 86400;
                                        r = snprintf (out_buf, sizeof (out_buf), "%d day%s %d hour%s %d minute%s" CRLF, 
                                                                days, days > 1 ? "s" : " ",
                                                                hours, hours > 1 ? "s" : " ",
@@ -201,7 +199,7 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
                                /* Else print the minutes and seconds. */
                                else {
                                        hours = uptime / 3600;
-                                       minutes = (uptime % 60) / 60;
+                                       minutes = uptime / 60 - hours * 3600;
                                        r = snprintf (out_buf, sizeof (out_buf), "%d hour%s %d minite%s %d second%s" CRLF, 
                                                                hours, hours > 1 ? "s" : " ",
                                                                minutes, minutes > 1 ? "s" : " ",
@@ -371,10 +369,14 @@ read_socket (struct bufferevent *bev, void *arg)
                                        session->state = STATE_REPLY;
                                }
                                if (session->state != STATE_LEARN) {
+                                       bufferevent_write (bev, END, sizeof (END) - 1);
                                        bufferevent_enable (bev, EV_WRITE);
                                }
                                g_strfreev (params);
                        }
+            else {
+                               bufferevent_enable (bev, EV_WRITE);
+            }
                        if (s != NULL) {
                                free (s);
                        }
index 2935fe8e6f6461a3cb27e005f7ad03a079c8f6ea..b2008c047e62a71df46eab44f194619626f18f55 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include <string.h>
 
 #include "fstring.h"
 
@@ -154,6 +155,21 @@ fstrcat (f_str_t *dest, f_str_t *src)
 
 }
 
+/*
+ * Make copy of string to 0-terminated string
+ */
+char* 
+fstrcstr (f_str_t *str, memory_pool_t *pool)
+{
+       char *res;
+       res = memory_pool_alloc (pool, str->len + 1);
+
+       memcpy (res, str->begin, str->len);
+       res[str->len] = 0;
+
+       return res;
+}
+
 /*
  * Push one character to fstr
  */
index 6840d9088adb0c8c8e283dd4a0234504d5da930e..896cd8dcfa83a182711dd3a27ba73efe85c390e0 100644 (file)
@@ -93,4 +93,10 @@ f_str_t* fstrgrow (memory_pool_t *pool, f_str_t *orig, size_t newlen);
  */
 uint32_t fstrhash (f_str_t *str);
 
+
+/*
+ * Make copy of string to 0-terminated string
+ */
+char* fstrcstr (f_str_t *str, memory_pool_t *pool);
+
 #endif
index c0a2a248751afa4518a6bbbe7d45224850349d08..830bc596095af9d74ab5d9ecaf4ed4c2224681bb 100644 (file)
@@ -337,6 +337,12 @@ statfile_pool_set_block (statfile_pool_t *pool, char *filename, uint32_t h1, uin
                        msg_debug ("statfile_pool_set_block: chain %u is full, starting expire", blocknum);
                        break;
                }
+               /* First try to find block in chain */
+               if (block->hash1 == h1 && block->hash2 == h2) {
+                       block->last_access = now - (time_t)header->create_time;
+                       block->value = value;
+                       return;
+               }
                /* Check whether we have a free block in chain */
                if (block->hash1 == 0 && block->hash2 == 0) {
                        /* Write new block here */
index afd2febd86ac8d72058f3382d7717d5276483878..45164467560813bc161fa5c750050645e2117956 100644 (file)
@@ -43,6 +43,7 @@ osb_tokenize_text (struct tokenizer *tokenizer, memory_pool_t *pool, f_str_t *in
                        hashpipe[i] = hashpipe[i - 1];
                }
                hashpipe[0] = fstrhash (&token);
+               msg_debug ("osb_tokenize_text: text token %s, hash: %d", fstrcstr (&token, pool), hashpipe[0]);
                
                for (i = 1; i < FEATURE_WINDOW_SIZE; i ++) {
                        h1 = hashpipe[0]* primes[0] + hashpipe[i] * primes[i<<1];
index 280ebd47719c2ec3b066ddadf3abc22cae5cdfa4..f0481e00d1cbf4ceb4c042a7f2691c36e3c8d15f 100644 (file)
@@ -60,11 +60,13 @@ get_next_word (f_str_t *buf, f_str_t *token)
 
        pos = token->begin;
        /* Skip non graph symbols */
-       while (remain-- && !g_ascii_isgraph (*pos ++)) {
+       while (remain-- && !g_ascii_isgraph (*pos)) {
                token->begin ++;
+               pos ++;
        }
-       while (remain-- && g_ascii_isgraph (*pos ++)) {
+       while (remain-- && g_ascii_isgraph (*pos)) {
                token->len ++;
+               pos ++;
        }
 
        if (token->len == 0) {