"Fossies" - the Fresh Open Source Software Archive

Member "bahamut-2.1.5/src/spamfilter.c" (28 May 2020, 16278 Bytes) of package /linux/privat/bahamut-2.1.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spamfilter.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.4_vs_2.1.5.

    1 /************************************************************************
    2  *   Bahamut IRCd, src/spamfilter.c
    3  *   Copyright (C) 2005-2018, Kobi Shmueli
    4  *
    5  *   See file AUTHORS in IRC package for additional names of
    6  *   the programmers.
    7  *
    8  *   This program is free software; you can redistribute it and/or modify
    9  *   it under the terms of the GNU General Public License as published by
   10  *   the Free Software Foundation; either version 1, or (at your option)
   11  *   any later version.
   12  *
   13  *   This program is distributed in the hope that it will be useful,
   14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  *   GNU General Public License for more details.
   17  *
   18  *   You should have received a copy of the GNU General Public License
   19  *   along with this program; if not, write to the Free Software
   20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   21  */
   22 
   23 #include "struct.h"
   24 #include "common.h"
   25 #include "numeric.h"
   26 #include "channel.h"
   27 #include "h.h"
   28 #include "spamfilter.h"
   29 #include "pcre.h"
   30 
   31 #define PCRE_COMP_FLAGS PCRE_EXTRA|PCRE_ANCHORED|PCRE_UNGREEDY
   32 #define SPAMFILTER_FILE "spamfilter.db"
   33 
   34 struct spam_filter
   35 {
   36     struct spam_filter *next;
   37     char *text;
   38     long flags;
   39     char *target;
   40     char *reason;
   41     char *id;
   42     pcre *re;
   43     unsigned int len;
   44     unsigned long matches;
   45 };
   46 
   47 struct spam_filter *spam_filters = NULL;
   48 static char buf2[BUFSIZE];
   49 time_t last_spamfilter_save = 0;
   50 
   51 /* load_spamfilter - Load the spamfilters
   52  *                   Returns: 1 = Success
   53  *                            0 = Failure
   54  */
   55 int load_spamfilter()
   56 {
   57     FILE *fle;
   58     char line[1024];
   59     char *para[MAXPARA + 1];
   60     int parc;
   61 
   62     if(!(fle = fopen(SPAMFILTER_FILE, "r")))
   63         return 0; /* Can't open file! */
   64 
   65     while(fgets(line, sizeof(line), fle))
   66     {
   67         char *tmp = strchr(line, '\n');
   68         if(!tmp)
   69             break;
   70         *tmp = '\0';
   71         tmp = line;
   72         parc = 0;
   73         while(*tmp)
   74         {
   75             while(*tmp==' ')
   76              *tmp++ = '\0';
   77 
   78             if(*tmp==':')
   79             {
   80                 para[parc++] = tmp + 1;
   81                 break;
   82             }
   83             para[parc++] = tmp;
   84             while(*tmp && *tmp!=' ')
   85                 tmp++;
   86         }
   87         para[parc + 1] = NULL;
   88         if(!mycmp(para[0],"SF"))
   89         {
   90             if(parc>4)
   91                 new_sf(para[1], atol(para[2]), para[4], para[3]);
   92             else if(parc>3)
   93                 new_sf(para[1], atol(para[2]), para[3], NULL);
   94         }
   95     }
   96     fclose(fle);
   97 
   98     return 1;
   99 }
  100 
  101 /* save_spamfilter - Save the spamfilters
  102  *                   Returns: 1 = Success
  103  *                            0 = Failure
  104  */
  105 int save_spamfilter()
  106 {
  107     FILE *fle;
  108     struct spam_filter *sf = spam_filters;
  109 
  110     fle = fopen(SPAMFILTER_FILE,"w");
  111     if(!fle)
  112         return 0;
  113 
  114     for(; sf; sf = sf->next)
  115     {
  116         if(sf->target)
  117             fprintf(fle, "SF %s %ld %s :%s\n", sf->text, sf->flags, sf->target, sf->reason);
  118         else
  119             fprintf(fle, "SF %s %ld :%s\n", sf->text, sf->flags, sf->reason);
  120     }
  121 
  122     fclose(fle);
  123 
  124     return 1;
  125 }
  126 
  127 /* spamfilter_sendserver - Send the spamfilter list on server connections */
  128 void spamfilter_sendserver(aClient *acptr)
  129 {
  130     struct spam_filter *sf;
  131 
  132     for(sf = spam_filters; sf; sf = sf->next)
  133     {
  134         if(sf->target)
  135             sendto_one(acptr, "SF %s %ld %s :%s", sf->text, sf->flags, sf->target, sf->reason);
  136         else
  137             sendto_one(acptr, "SF %s %ld :%s", sf->text, sf->flags, sf->reason);
  138     }
  139 }
  140 
  141 /* m_spamops - Send a SPAM_LEV notice to all local opers and propgate it to other servers
  142    parv[1] = message
  143  */
  144 int m_spamops(aClient *cptr, aClient *sptr, int parc, char *parv[])
  145 {
  146     char       *message = parc > 1 ? parv[1] : NULL;
  147 
  148     if (BadPtr(message))
  149     {
  150         return 0;
  151     }
  152 
  153     if (!IsServer(sptr))
  154     {
  155         sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
  156         return 0;
  157     }
  158     if (strlen(message) > TOPICLEN)
  159         message[TOPICLEN] = '\0';
  160     sendto_serv_butone_super(cptr, 0, ":%s SPAMOPS :%s", parv[0], message);
  161     sendto_realops_lev(SPAM_LEV, "from %s: %s", parv[0], message);
  162 
  163     return 0;
  164 }
  165 
  166 /* check_sf - checks if a text matches a spamfilter pattern
  167               Returns: 1 = User message has been blocked.
  168                        2 = User has been killed and the message has been blocked.
  169                        0 = User message was not blocked (but it doesn't mean we didn't have a non-block match).
  170  */
  171 int check_sf(aClient *cptr, char *text, char *caction, int action, char *target)
  172 {
  173     struct spam_filter *p = spam_filters;
  174     unsigned short blocked = 0;
  175     unsigned short reported = 0;
  176     unsigned short warned = 0;
  177     unsigned short matched;
  178     char stripamsg[512];
  179     char stripcmsg[512];
  180     unsigned int len = 0; /* For regexp */
  181     int ovector[30]; /* For regexp */
  182     char *action_text;
  183     char *textptr;
  184 
  185     if(IsAnOper(cptr))
  186         return 0;
  187 
  188     stripamsg[0] = '\0';
  189     stripcmsg[0] = '\0';
  190 
  191     for(; p; p = p->next)
  192     {
  193         if(!(p->flags & action))
  194             continue;
  195         if(IsRegNick(cptr) && !(p->flags & SF_FLAG_MATCHREG))
  196             continue;
  197         if(p->target && match(p->target,target))
  198             continue;
  199         if(p->flags & SF_FLAG_STRIPALL)
  200         {
  201             if(stripamsg[0]=='\0')
  202             {
  203                 textptr = text;
  204                 while(*textptr==' ') textptr++;
  205                 if(*textptr && *target && !strncasecmp(textptr,target,strlen(target)))
  206                 {
  207                     textptr += strlen(target);
  208                 }
  209                 stripall(stripamsg, textptr);
  210             }
  211             matched = !match(p->text,stripamsg);
  212         }
  213         else if(p->flags & SF_FLAG_STRIPCTRL)
  214         {
  215             if(stripcmsg[0]=='\0')
  216                 stripcolors(stripcmsg, text);
  217             matched = !match(p->text,stripcmsg);
  218         }
  219         else if(p->flags & SF_FLAG_REGEXP)
  220         {
  221             if(!len)
  222                 len = strlen(text);
  223             if(pcre_exec(p->re, NULL, text, len, 0, 0, ovector, 30) > 0)
  224                 matched = 1;
  225             else
  226                 matched = 0;
  227 
  228         }
  229         else matched = !match(p->text,text);
  230         if(matched) {
  231             if(p->matches < ULONG_MAX) p->matches++;
  232             if(p->flags & SF_ACT_LAG)
  233                 cptr->since += 4;
  234             if(p->flags & SF_ACT_BLOCK)
  235                 blocked = 1;
  236             if(!warned && (p->flags & SF_ACT_WARN))
  237             {
  238                 sendto_one(cptr, ":%s NOTICE %s :*** Notice -- Your message has %s. Reason: %s",
  239                            me.name, cptr->name, blocked?"been blocked":"triggered a warning",
  240                            p->reason?p->reason:"<none>");
  241                 sendto_one(cptr, ":%s NOTICE %s :*** Notice -- Please visit %s for more information.",
  242                            me.name, cptr->name, SpamFilter_URL);
  243                 warned++;
  244             }
  245             if(!reported && (p->flags & SF_ACT_REPORT))
  246             {
  247                 if((p->flags & SF_ACT_BLOCK) && (p->flags & SF_ACT_AKILL))
  248                     action_text = " (blocked+akilled)";
  249                 else if(action!=SF_CMD_QUIT && (p->flags & SF_ACT_BLOCK) && (p->flags & SF_ACT_KILL))
  250                     action_text = " (blocked+killed)";
  251                 else if(p->flags & SF_ACT_BLOCK)
  252                     action_text = " (blocked)";
  253                 else if(p->flags & SF_ACT_AKILL)
  254                     action_text = " (akilled)";
  255                 else if(action!=SF_CMD_QUIT && p->flags & SF_ACT_KILL)
  256                     action_text = " (killed)";
  257                 else
  258                     action_text = "";
  259                 if(IsPerson(cptr))
  260                 {
  261                     sendto_realops_lev(SPAM_LEV, "spamfilter %s: %s by %s!%s@%s to %s%s --> %s", p->id?p->id:p->text,
  262                                        caction, cptr->name, cptr->user->username, cptr->user->host,
  263                                        target, action_text, text);
  264                     sendto_serv_butone(NULL, ":%s SPAMOPS :spamfilter %s: %s by %s!%s@%s to %s%s --> %s",
  265                                        me.name, p->id?p->id:p->text, caction, cptr->name, cptr->user->username,
  266                                        cptr->user->host, target, action_text, text);
  267                 }
  268                 else
  269                 {
  270                     sendto_realops_lev(SPAM_LEV, "spamfilter %s: %s by %s to %s%s --> %s", p->id?p->id:p->text,
  271                                        caction, cptr->name, target, action_text, text);
  272                     sendto_serv_butone(NULL, ":%s SPAMOPS :spamfilter %s: %s by %s to %s%s --> %s",
  273                                        me.name, p->id?p->id:p->text, caction, cptr->name, target, action_text, text);
  274                 }
  275                 reported++;
  276             }
  277             if(p->flags & SF_ACT_AKILL)
  278             {
  279                 if(aliastab[AII_OS].client)
  280                     sendto_one(aliastab[AII_OS].client->from, ":%s OS SFAKILL %s!%s@%s %ld %s", me.name, cptr->name,
  281                                cptr->user?cptr->user->username:"<none>", cptr->hostip,
  282                                cptr->tsinfo, p->reason?p->reason:"<none>");
  283             }
  284             if(p->flags & SF_ACT_KILL)
  285             {
  286                 blocked = 2;
  287                 if(action!=SF_CMD_QUIT)
  288                 {
  289                     ircsprintf(buf2, "Local kill by %s (%s)", me.name, p->reason?p->reason:"<none>");
  290                     exit_client(cptr, cptr, cptr, buf2);
  291                     return blocked;
  292                 }
  293             }
  294             if(p->flags & SF_FLAG_BREAK)
  295                 return blocked;
  296         }
  297     }
  298 
  299     return blocked;
  300 }
  301 
  302 struct spam_filter *find_sf(char *text)
  303 {
  304     struct spam_filter *p = spam_filters;
  305 
  306     for(; p; p = p->next)
  307     {
  308         if(!mycmp(p->text,text))
  309             return p; /* Found! */
  310     }
  311 
  312     return NULL; /* Not found */
  313 }
  314 
  315 struct spam_filter *new_sf(char *text, long flags, char *reason, char *target)
  316 {
  317     struct spam_filter *p;
  318     int erroroffset;
  319     const char *error;
  320     pcre *re;
  321     unsigned int len = 0; /* For the spamfilter id */
  322 
  323     if(flags & SF_FLAG_REGEXP)
  324     {
  325         re = pcre_compile(text, PCRE_COMP_FLAGS, &error, &erroroffset, NULL);
  326         if(!re)
  327             return NULL; /* error! */
  328     }
  329     else
  330         re = NULL;
  331 
  332     p = find_sf(text);
  333     if(p)
  334     {
  335         if(p->target)
  336             MyFree(p->target);
  337         if(p->reason)
  338             MyFree(p->reason);
  339         if(p->id)
  340             MyFree(p->id);
  341         if(p->re)
  342             pcre_free(p->re);
  343     }
  344     else
  345     {
  346         p = MyMalloc(sizeof(struct spam_filter));
  347         p->next = spam_filters;
  348         spam_filters = p;
  349         p->len = strlen(text); /* We only need the length for REGEXP entries so we won't check it every match-check but we use it for MyMalloc anyway so I put it here -Kobi. */
  350         p->text = MyMalloc(p->len + 1);
  351         strcpy(p->text, text);
  352         p->matches = 0;
  353     }
  354     p->flags = flags;
  355     p->re = re;
  356     if(reason)
  357     {
  358         p->reason = MyMalloc(strlen(reason) + 1);
  359         strcpy(p->reason, reason);
  360         len = 1;
  361         if(reason[0] == '[')
  362         {
  363             while(reason[len]!=']' && reason[len]!='\0')
  364                 len++;
  365         }
  366         if(len > 1 && reason[len]==']')
  367         {
  368             p->id = MyMalloc(len-1);
  369             strncpy(p->id, &reason[1], len - 1);
  370             p->id[len-1] = '\0';
  371         }
  372         else p->id = NULL;
  373     }
  374     else
  375     {
  376         p->reason = NULL;
  377         p->id = NULL;
  378     }
  379     if(target)
  380     {
  381         p->target = MyMalloc(strlen(target) + 1);
  382         strcpy(p->target, target);
  383     }
  384     else p->target = NULL;
  385 
  386     return p;
  387 }
  388 
  389 int del_sf(char *text)
  390 {
  391     struct spam_filter *p, *pprev, *pn;
  392 
  393     for(p = spam_filters, pprev = NULL; p; pprev = p, p = pn)
  394     {
  395         pn = p->next;
  396         if(!mycmp(p->text,text))
  397         {
  398             if(pprev)
  399                 pprev->next = p->next;
  400             else
  401                 spam_filters = p->next;
  402             if(p->text)
  403                 MyFree(p->text);
  404             if(p->target)
  405                 MyFree(p->target);
  406             if(p->reason)
  407                 MyFree(p->reason);
  408             if(p->id)
  409                 MyFree(p->id);
  410             if(p->re)
  411                 pcre_free(p->re);
  412             MyFree(p);
  413             return 1; /* Success */
  414         }
  415     }
  416 
  417     return 0; /* Failure */
  418 }
  419 
  420 /* m_sf - Spam Filter
  421  * parv[1] - Text
  422  * parv[2] - Flags (0 to delete)
  423  * parv[3] - (Optional) Target
  424  * parv[4] or parv[3] - Reason
  425  */
  426 int m_sf(aClient *cptr, aClient *sptr, int parc, char *parv[])
  427 {
  428     AliasInfo *ai = &aliastab[AII_OS];
  429 
  430     if(!IsServer(sptr) || parc<3)
  431         return 0;
  432 
  433     if(!IsULine(sptr) && ai->client && ai->client->from!=cptr->from)
  434     {
  435         /*
  436          * We don't accept commands from a non-services direction.
  437          * Also, we remove non-existed spamfilters if they come from this location.
  438          * Note: we don't need to worry about existed spamfilters on the other side
  439          * because they will be overrided anyway.
  440          */
  441         if(!find_sf(parv[1]) && mycmp(parv[2], "0"))
  442             sendto_one(cptr, ":%s SF %s 0", me.name, parv[1]);
  443         return 0;
  444     }
  445     if(mycmp(parv[2], "0"))
  446     {
  447         if(parc>4)
  448             new_sf(parv[1], atol(parv[2]), parv[4], parv[3]);
  449         else
  450             new_sf(parv[1], atol(parv[2]), parv[3], NULL);
  451     }
  452     else
  453         del_sf(parv[1]);
  454 
  455     if(parc>4)
  456         sendto_serv_butone(cptr, ":%s SF %s %s %s :%s", parv[0], parv[1], parv[2], parv[3], parv[4]);
  457     else if(parc>3)
  458         sendto_serv_butone(cptr, ":%s SF %s %s :%s", parv[0], parv[1], parv[2], parv[3]);
  459     else
  460         sendto_serv_butone(cptr, ":%s SF %s %s", parv[0], parv[1], parv[2]);
  461 
  462     if(NOW > last_spamfilter_save + 300) {
  463       last_spamfilter_save = NOW;
  464       save_spamfilter();
  465     }
  466 
  467     return 0;
  468 }
  469 
  470 /* report_spamfilters - send /stats S (spamfilter list) output to opers */
  471 int report_spamfilters(aClient *cptr, aClient *sptr, int parc, char *parv[])
  472 {
  473     struct spam_filter *sf = spam_filters;
  474 
  475     for(; sf; sf = sf->next)
  476     {
  477         sendto_one(sptr, rpl_str(RPL_STATSSLINE), me.name,
  478                    sptr->name, "S", sf->text, sf->flags,
  479                    sf->target?sf->target:"<NONE>", sf->matches,
  480                    sf->reason);
  481     }
  482 
  483     return 0;
  484 }
  485 
  486 /* Strip colors and other control codes from a text */
  487 void stripcolors(char new[512], char *org)
  488 {
  489     int len = 0;
  490 
  491     for(; (*org && len < 512); org++)
  492     {
  493         if(*org == '\003')
  494         {
  495             // Color codes: 1-2 digits, then optionally followed by a comma and an additional 1-2 digits
  496             org++;
  497             if(*org && IsDigit(*org))
  498             {
  499                 org++;
  500                 if(*org && IsDigit(*org))
  501                     org++;
  502                 if(*org && *org == ',' && IsDigit(*(org + 1)))
  503                 {
  504                     org = org + 2;
  505                     if(*org && IsDigit(*org))
  506                         org++;
  507                 }
  508             }
  509         }
  510         if(*org < 32)
  511             continue;
  512         new[len++] = *org;
  513     }
  514     new[len] = '\0';
  515 }
  516 
  517 /* Strip all "special" chars */
  518 void stripall(char new[512], char *org)
  519 {
  520 #define fstripall(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || IsDigit(c) || c == '-' || c == '/' || c == '.' || c== '$' || c == '(') /* to strip everything ;) */
  521     int len = 0;
  522 
  523     for(; (*org && len<512); org++)
  524     {
  525         if(*org == '\003')
  526         {
  527             // Color codes: 1-2 digits, then optionally followed by a comma and an additional 1-2 digits
  528             org++;
  529             if(*org && IsDigit(*org))
  530             {
  531                 org++;
  532                 if(*org && IsDigit(*org))
  533                     org++;
  534                 if(*org && *org == ',' && IsDigit(*(org + 1)))
  535                 {
  536                     org = org + 2;
  537                     if(*org && IsDigit(*org))
  538                         org++;
  539                 }
  540             }
  541         }
  542         if(!fstripall(*org))
  543             continue;
  544         new[len++] = *org;
  545     }
  546     new[len] = '\0';
  547 }