28a9a8d742804f510ced72809e1986581d768237
[citadel.git] / citadel / modules / dspam / serv_dspam.c
1 /*
2  * This module glues libDSpam to the Citadel server in order to implement
3  * DSPAM Spamchecking 
4  *
5  * Copyright (c) 2012 by the citadel.org team
6  *
7  *  This program is open source software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 3.
9  *  
10  *  
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  
18  *  
19  *  
20  */
21
22 #include "sysdep.h"
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <pwd.h>
29 #include <errno.h>
30 #include <sys/types.h>
31
32 #if TIME_WITH_SYS_TIME
33 # include <sys/time.h>
34 # include <time.h>
35 #else
36 # if HAVE_SYS_TIME_H
37 #  include <sys/time.h>
38 # else
39 #  include <time.h>
40 # endif
41 #endif
42
43 #include <sys/wait.h>
44 #include <string.h>
45 #include <limits.h>
46 #include <libcitadel.h>
47 #include "citadel.h"
48 #include "server.h"
49 #include "citserver.h"
50 #include "support.h"
51 #include "config.h"
52 #include "database.h"
53 #include "msgbase.h"
54 #include "internet_addressing.h"
55
56
57 #include "ctdl_module.h"
58
59
60 #ifdef HAVE_LIBDSPAM
61 #define CONFIG_DEFAULT file_dpsam_conf
62 #define LOGDIR file_dspam_log
63
64
65 //#define HAVE_CONFIG_H
66 #include <dspam/libdspam.h>
67 //#define HAVE_CONFIG_H
68
69 typedef struct stringlist stringlist;
70
71 struct stringlist {
72         char *Str;
73         long len;
74         stringlist *Next;
75 };
76         
77
78 /*
79  * Citadel protocol to manage sieve scripts.
80  * This is basically a simplified (read: doesn't resemble IMAP) version
81  * of the 'managesieve' protocol.
82  */
83 void cmd_tspam(char *argbuf) {
84         char buf[SIZ];
85         long len;
86         long count;
87         stringlist *Messages; 
88         stringlist *NextMsg; 
89
90         Messages = NULL;
91         NextMsg = NULL;
92         count = 0;
93         if (CtdlAccessCheck(ac_room_aide)) return;
94         if (atoi(argbuf) == 0) {
95                 cprintf("%d Ok.\n", CIT_OK);
96                 return;
97         }
98         cprintf("%d Send info...\n", SEND_LISTING);
99
100         do {
101                 len = client_getln(buf, sizeof buf);
102                 if (strcmp(buf, "000")) {
103                         if (Messages == NULL) {
104                                 Messages = malloc (sizeof (stringlist));
105                                 NextMsg = Messages;
106                         }
107                         else {
108                                 Messages->Next = malloc (sizeof (stringlist));
109                                 NextMsg = NextMsg->Next;
110                         }
111                         NextMsg->Next = NULL;
112                         NextMsg->Str = malloc (len+1);
113                         NextMsg->len = len;
114                         memcpy (NextMsg->Str, buf, len + 1);/// maybe split spam /ham per line?
115                         count++;
116                 }
117         } while (strcmp(buf, "000"));
118 /// is there a way to filter foreachmessage by a list?
119         /* tag mails as spam or Ham */
120         /* probably do: dspam_init(ctdl_dspam_dir); dspam_process dspam_addattribute; dspam_destroy*/
121         // extract DSS_ERROR or DSS_CORPUS from the commandline. error->ham; corpus -> spam?
122         /// todo: send answer listing...
123 }
124
125
126
127 void ctdl_dspam_init(void) {
128
129 ///     libdspam_init("bdb");/* <which database backend do we prefer? */
130
131 }
132
133 void dspam_do_msg(long msgnum, void *userdata) 
134 {
135         char *msgtext;
136         DSPAM_CTX *CTX;                 /* DSPAM Context */
137         struct CtdlMessage *msg;
138         struct _ds_spam_signature SIG;        /* signature */
139
140         CTX = *(DSPAM_CTX**) userdata;
141         msg = CtdlFetchMessage(msgnum, 0);
142         if (msg == NULL) return;
143
144
145         /* Message */
146         CC->redirect_buffer = malloc(SIZ);
147         CC->redirect_len = 0;
148         CC->redirect_alloc = SIZ;
149         CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0);
150         msgtext = CC->redirect_buffer;
151 // don't need?  msglen = CC->redirect_len;
152         CC->redirect_buffer = NULL;
153         CC->redirect_len = 0;
154         CC->redirect_alloc = 0;
155
156         /* Call DSPAM's processor with the message text */
157         if (dspam_process (CTX, msgtext) != 0)
158         {
159                 free(msgtext);
160                 syslog(LOG_CRIT, "ERROR: dspam_process failed");
161                 return;
162         }
163         if (CTX->signature == NULL)
164         {
165                 syslog(LOG_CRIT,"No signature provided\n");
166         }
167         else
168         {
169 /* Copy to a safe place */
170
171                 msg->cm_fields[eErrorMsg] = malloc (CTX->signature->length * 2);
172                 CtdlEncodeBase64(msg->cm_fields[eErrorMsg], CTX->signature->data, CTX->signature->length, 0);
173         }
174         free(msgtext);
175
176         SIG.length = CTX->signature->length;
177         /* Print processing results */
178         syslog(LOG_DEBUG, "Probability: %2.4f Confidence: %2.4f, Result: %s\n",
179                 CTX->probability,
180                 CTX->confidence,
181                 (CTX->result == DSR_ISSPAM) ? "Spam" : "Innocent");
182
183         //// todo: put signature into the citadel message
184         //// todo: save message; destroy message.
185 }
186
187 int serv_dspam_room(struct ctdlroom *room)
188 {
189         DSPAM_CTX *CTX;                 /* DSPAM Context */
190
191         /* scan for spam; do */
192         /* probably do: dspam_init; dspam_process dspam_addattribute; dspam_destroy*/
193 //DSS_NONE
194 //#define       DSR_ISSPAM              0x01
195 //#define DSR_ISINNOCENT                0x02
196 // dspam_init (cc->username, NULL, ctdl_dspam_home, DSM_PROCESS,
197         //                  DSF_SIGNATURE | DSF_NOISE);
198         /// todo: if roomname = spam / ham -> learn!
199         if ((room->QRflags & QR_PRIVATE) &&/* Are we sending to a private mailbox? */
200             (strstr(room->QRname, ".Mail")!=NULL))
201
202         {
203                 char User[64];
204                 // maybe we should better get our realname here?
205                 snprintf(User, 64, "%ld", room->QRroomaide);
206                 extract_token(User, room->QRname, 0, '.', sizeof(User));
207                 CTX = dspam_init(User, 
208                                  NULL,
209                                  ctdl_dspam_dir, 
210                                  DSM_PROCESS, 
211                                  DSF_SIGNATURE | DSF_NOISE);
212         }
213         else return 0;//// 
214         /// else -> todo: global user for public rooms etc.
215         if (CTX == NULL)
216         {
217                 syslog(LOG_CRIT, "ERROR: dspam_init failed!\n");
218                 return ERROR + INTERNAL_ERROR;
219         }
220         /* Use graham and robinson algorithms, graham's p-values */
221         CTX->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM;
222
223         /* Use CHAIN tokenizer */
224         CTX->tokenizer = DSZ_CHAIN;
225
226         CtdlForEachMessage(MSGS_GT, 1, NULL, NULL, NULL,
227                            dspam_do_msg,
228                            (void *) &CTX);
229
230         return 0;
231 }
232
233 void serv_dspam_shutdown (void)
234 {
235         libdspam_shutdown ();
236 }
237 #endif  /* HAVE_LIBDSPAM */
238
239 CTDL_MODULE_INIT(dspam)
240 {
241         return "disabled.";
242         if (!threading)
243         {
244 #ifdef HAVE_LIBDSPAM
245
246                 ctdl_dspam_init();
247                 CtdlRegisterCleanupHook(serv_dspam_shutdown);
248                 CtdlRegisterProtoHook(cmd_tspam, "SPAM", "Tag Message as Spam/Ham to learn DSPAM");
249
250                 CtdlRegisterRoomHook(serv_dspam_room);
251
252                 ///CtdlRegisterSessionHook(perform_dspam_processing, EVT_HOUSE);
253
254 #else   /* HAVE_LIBDSPAM */
255
256                 syslog(LOG_INFO, "This server is missing libdspam Spam filtering will be disabled.\n");
257
258 #endif  /* HAVE_LIBDSPAM */
259         }
260         
261         /* return our module name for the log */
262         return "dspam";
263 }
264