some minor updates
[claws.git] / src / scoring.c
1 #include <ctype.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <stdio.h>
6 #include "defs.h"
7 #include "intl.h"
8 #include "utils.h"
9 #include "procheader.h"
10 #include "matcher.h"
11 #include "scoring.h"
12 #include "prefs.h"
13 #include "folder.h"
14
15 #define PREFSBUFSIZE            1024
16
17
18 GSList * global_scoring;
19
20 ScoringProp * scoringprop_parse(gchar ** str)
21 {
22         gchar * tmp;
23         gint key;
24         ScoringProp * scoring;
25         gint score;
26         MatcherList * matchers;
27
28         tmp = * str;
29
30         matchers = matcherlist_parse(&tmp);
31         if (tmp == NULL) {
32                 * str = NULL;
33                 return NULL;
34         }
35
36         key = matcher_parse_keyword(&tmp);
37
38         if (tmp == NULL) {
39                 matcherlist_free(matchers);
40                 * str = NULL;
41                 return NULL;
42         }
43
44         if (key != MATCHING_SCORE) {
45                 matcherlist_free(matchers);
46                 * str = NULL;
47                 return NULL;
48         }
49
50         score = matcher_parse_number(&tmp);
51
52         if (tmp == NULL) {
53                 matcherlist_free(matchers);
54                 * str = NULL;
55                 return NULL;
56         }
57
58         scoring = scoringprop_new(matchers, score);
59
60         * str = tmp;
61         return scoring;
62 }
63
64
65 ScoringProp * scoringprop_new(MatcherList * matchers, int score)
66 {
67         ScoringProp * scoring;
68
69         scoring = g_new0(ScoringProp, 1);
70         scoring->matchers = matchers;
71         scoring->score = score;
72
73         return scoring;
74 }
75
76 void scoringprop_free(ScoringProp * prop)
77 {
78         matcherlist_free(prop->matchers);
79         g_free(prop);
80 }
81
82 gint scoringprop_score_message(ScoringProp * scoring, MsgInfo * info)
83 {
84         if (matcherlist_match(scoring->matchers, info))
85                 return scoring->score;
86         else
87                 return 0;
88 }
89
90 gint score_message(GSList * scoring_list, MsgInfo * info)
91 {
92         gint score = 0;
93         gint add_score;
94         GSList * l;
95
96         for(l = scoring_list ; l != NULL ; l = g_slist_next(l)) {
97                 ScoringProp * scoring = (ScoringProp *) l->data;
98                 
99                 add_score = (scoringprop_score_message(scoring, info));
100                 if (add_score == MAX_SCORE || add_score == MIN_SCORE) {
101                         score = add_score;
102                         break;
103                 }
104                 score += add_score;
105         }
106         return score;
107 }
108
109 #if 0
110 static void scoringprop_print(ScoringProp * prop)
111 {
112         GSList * l;
113
114         if (prop == NULL) {
115                 printf("no scoring\n");
116                 return;
117         }
118
119         printf("----- scoring ------\n");
120         for(l = prop->matchers ; l != NULL ; l = g_slist_next(l)) {
121                 matcherprop_print((MatcherProp *) l->data);
122         }
123         printf("cond: %s\n", prop->bool_and ? "and" : "or");
124         printf("score: %i\n", prop->score);
125 }
126 #endif
127
128 /*
129   syntax for scoring config
130
131   file ~/.sylpheed/scoringrc
132
133   header "x-mailing" match "toto" score -10
134   subject match "regexp" & to regexp "regexp" score 50
135   subject match "regexp" | to regexpcase "regexp" | age_sup 5 score 30
136
137   if score is = MIN_SCORE (-999), no more match is done in the list
138   if score is = MAX_SCORE (-999), no more match is done in the list
139  */
140
141 /*
142 void prefs_scoring_read_config(void)
143 {
144         gchar *rcpath;
145         FILE *fp;
146         gchar buf[PREFSBUFSIZE];
147
148         debug_print(_("Reading headers configuration...\n"));
149
150         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, SCORING_RC, NULL);
151         if ((fp = fopen(rcpath, "r")) == NULL) {
152                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
153                 g_free(rcpath);
154                 prefs_scoring = NULL;
155                 return;
156         }
157         g_free(rcpath);
158
159         while (prefs_scoring != NULL) {
160                 ScoringProp * scoring = (ScoringProp *) prefs_scoring->data;
161                 scoringprop_free(scoring);
162                 prefs_scoring = g_slist_remove(prefs_scoring, scoring);
163         }
164
165         while (fgets(buf, sizeof(buf), fp) != NULL) {
166                 ScoringProp * scoring;
167                 gchar * tmp;
168
169                 g_strchomp(buf);
170
171                 if ((*buf != '#') && (*buf != '\0')) {
172                         tmp = buf;
173                         scoring = scoringprop_parse(&tmp);
174                         if (tmp != NULL) {
175                                 prefs_scoring = g_slist_append(prefs_scoring,
176                                                                scoring);
177                         }
178                         else {
179                                 g_warning(_("syntax error : %s\n"), buf);
180                         }
181                 }
182         }
183
184         fclose(fp);
185 }
186 */
187
188 gchar * scoringprop_to_string(ScoringProp * prop)
189 {
190         gchar * list_str;
191         gchar * score_str;
192         gchar * scoring_str;
193
194         list_str = matcherlist_to_string(prop->matchers);
195
196         if (list_str == NULL)
197                 return NULL;
198
199         score_str = itos(prop->score);
200         scoring_str = g_strconcat(list_str, " score ", score_str, NULL);
201         g_free(list_str);
202
203         return scoring_str;
204 }
205
206 /*
207 void prefs_scoring_write_config(void)
208 {
209         gchar *rcpath;
210         PrefFile *pfile;
211         GSList *cur;
212         ScoringProp * prop;
213
214         debug_print(_("Writing scoring configuration...\n"));
215
216         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, SCORING_RC, NULL);
217
218         if ((pfile = prefs_write_open(rcpath)) == NULL) {
219                 g_warning(_("failed to write configuration to file\n"));
220                 g_free(rcpath);
221                 return;
222         }
223
224         for (cur = prefs_scoring; cur != NULL; cur = cur->next) {
225                 gchar *scoring_str;
226
227                 prop = (ScoringProp *) cur->data;
228                 scoring_str = scoringprop_to_string(prop);
229                 if (fputs(scoring_str, pfile->fp) == EOF ||
230                     fputc('\n', pfile->fp) == EOF) {
231                         FILE_OP_ERROR(rcpath, "fputs || fputc");
232                         prefs_write_close_revert(pfile);
233                         g_free(rcpath);
234                         g_free(scoring_str);
235                         return;
236                 }
237                 g_free(scoring_str);
238         }
239
240         g_free(rcpath);
241
242         if (prefs_write_close(pfile) < 0) {
243                 g_warning(_("failed to write configuration to file\n"));
244                 return;
245         }
246 }
247 */
248
249 void prefs_scoring_free(GSList * prefs_scoring)
250 {
251         while (prefs_scoring != NULL) {
252                 ScoringProp * scoring = (ScoringProp *) prefs_scoring->data;
253                 scoringprop_free(scoring);
254                 prefs_scoring = g_slist_remove(prefs_scoring, scoring);
255         }
256 }
257
258 static gboolean prefs_scoring_free_func(GNode *node, gpointer data)
259 {
260         FolderItem *item = node->data;
261
262         prefs_scoring_free(item->prefs->scoring);
263         item->prefs->scoring = NULL;
264
265         return FALSE;
266 }
267
268 static void prefs_scoring_clear()
269 {
270         GList * cur;
271
272         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
273                 Folder *folder;
274
275                 folder = (Folder *) cur->data;
276                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
277                                 prefs_scoring_free_func, NULL);
278         }
279
280         prefs_scoring_free(global_scoring);
281         global_scoring = NULL;
282 }
283
284 void prefs_scoring_read_config(void)
285 {
286         gchar *rcpath;
287         FILE *fp;
288         gchar buf[PREFSBUFSIZE];
289         GSList * prefs_scoring = NULL;
290         FolderItem * item = NULL;
291
292         debug_print(_("Reading headers configuration...\n"));
293
294         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, SCORING_RC, NULL);
295         if ((fp = fopen(rcpath, "r")) == NULL) {
296                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
297                 g_free(rcpath);
298                 prefs_scoring = NULL;
299                 return;
300         }
301         g_free(rcpath);
302
303         prefs_scoring_clear();
304
305         while (fgets(buf, sizeof(buf), fp) != NULL) {
306                 ScoringProp * scoring;
307                 gchar * tmp;
308
309                 g_strchomp(buf);
310
311                 if (g_strcasecmp(buf, "[global]") == 0) {
312                         if (item != NULL) {
313                                 item->prefs->scoring = prefs_scoring;
314                                 item = NULL;
315                                 prefs_scoring = global_scoring;
316                         }
317                 }
318                 else if ((*buf == '[') && buf[strlen(buf) - 1] == ']') {
319                         gchar * id;
320
321                         if (item == NULL)
322                                 global_scoring = prefs_scoring;
323                         else
324                                 item->prefs->scoring = prefs_scoring;
325
326                         id = buf + 1;
327                         id[strlen(id) - 1] = '\0';
328
329                         item = folder_find_item_from_identifier(id);
330                         if (item == NULL)
331                                 prefs_scoring = global_scoring;
332                         else
333                                 prefs_scoring = item->prefs->scoring;
334                 }
335                 else if ((*buf != '#') && (*buf != '\0')) {
336                         tmp = buf;
337                         scoring = scoringprop_parse(&tmp);
338                         if (tmp != NULL) {
339                                 prefs_scoring = g_slist_append(prefs_scoring,
340                                                                scoring);
341                         }
342                         else {
343                                 /* debug */
344                                 g_warning(_("syntax error : %s\n"), buf);
345                         }
346                 }
347         }
348
349         if (item == NULL)
350                 global_scoring = prefs_scoring;
351         else
352                 item->prefs->scoring = prefs_scoring;
353
354         fclose(fp);
355 }
356
357 static void prefs_scoring_write(FILE * fp, GSList * prefs_scoring)
358 {
359         GSList * cur;
360
361         for (cur = prefs_scoring; cur != NULL; cur = cur->next) {
362                 gchar *scoring_str;
363                 ScoringProp * prop;
364
365                 prop = (ScoringProp *) cur->data;
366                 scoring_str = scoringprop_to_string(prop);
367                 if (fputs(scoring_str, fp) == EOF ||
368                     fputc('\n', fp) == EOF) {
369                         FILE_OP_ERROR("scoring config", "fputs || fputc");
370                         g_free(scoring_str);
371                         return;
372                 }
373                 g_free(scoring_str);
374         }
375 }
376
377 static gboolean prefs_scoring_write_func(GNode *node, gpointer data)
378 {
379         FolderItem *item = node->data;
380         FILE * fp = data;
381
382         if (item->prefs->scoring != NULL) {
383                 gchar * id = folder_item_get_identifier(item);
384
385                 fprintf(fp, "[%s]\n", id);
386                 g_free(id);
387
388                 prefs_scoring_write(fp, item->prefs->scoring);
389
390                 fputc('\n', fp);
391         }
392
393         return FALSE;
394 }
395
396 static void prefs_scoring_save(FILE * fp)
397 {
398         GList * cur;
399
400         fputs("[global]\n", fp);
401         prefs_scoring_write(fp, global_scoring);
402         fputc('\n', fp);
403
404         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
405                 Folder *folder;
406
407                 folder = (Folder *) cur->data;
408                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
409                                 prefs_scoring_write_func, fp);
410         }
411 }
412
413 void prefs_scoring_write_config(void)
414 {
415         gchar *rcpath;
416         PrefFile *pfile;
417         GSList *cur;
418         ScoringProp * prop;
419
420         debug_print(_("Writing scoring configuration...\n"));
421
422         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, SCORING_RC, NULL);
423
424         if ((pfile = prefs_write_open(rcpath)) == NULL) {
425                 g_warning(_("failed to write configuration to file\n"));
426                 g_free(rcpath);
427                 return;
428         }
429
430
431         prefs_scoring_save(pfile->fp);
432
433         g_free(rcpath);
434
435         if (prefs_write_close(pfile) < 0) {
436                 g_warning(_("failed to write configuration to file\n"));
437                 return;
438         }
439 }