1 #include <string.h>
2 #include <stdio.h>
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <ctype.h>
7 
8 #ifndef WIN32
9 #include <sys/time.h>
10 #include <unistd.h>
11 #endif
12 
13 #include "send-mod.h"
14 
HOOK_HANDLER(hook_tolist_sort)15 HOOK_HANDLER(hook_tolist_sort)
16 {
17     if (!LMAPI->get_bool("sort-tolist")) return HOOK_RESULT_OK;
18 
19     LMAPI->sort_tolist();
20     return HOOK_RESULT_OK;
21 }
22 
HOOK_HANDLER(hook_tolist_build_tolist)23 HOOK_HANDLER(hook_tolist_build_tolist)
24 {
25    char *extra_lists;
26 
27    LMAPI->new_tolist();
28 
29    LMAPI->add_from_list_all(LMAPI->get_string("list"));
30 
31    if(LMAPI->get_bool("megalist"))
32        return HOOK_RESULT_OK;
33 
34    /* Okay.. Heres' how we bounce to multiple lists */
35    extra_lists = strdup(LMAPI->get_string("cc-lists"));
36    if(extra_lists) {
37        char *l = strtok(extra_lists, ":");
38        while(l) {
39            if(LMAPI->list_valid(l)) {
40               LMAPI->add_from_list_all(l);
41            }
42            l = strtok(NULL,":");
43        }
44    }
45    free(extra_lists);
46 
47    return(HOOK_RESULT_OK);
48 }
49 
HOOK_HANDLER(hook_tolist_remove_vacationers)50 HOOK_HANDLER(hook_tolist_remove_vacationers)
51 {
52    LMAPI->remove_flagged_all_prilist("VACATION",LMAPI->get_string("list"));
53    LMAPI->remove_flagged_all_prilist("DIAGNOSTIC",LMAPI->get_string("list"));
54 
55    return(HOOK_RESULT_OK);
56 }
57 
HOOK_HANDLER(hook_presend_check_moderate)58 HOOK_HANDLER(hook_presend_check_moderate)
59 {
60    if (LMAPI->get_bool("moderated")) {
61       const char *tempchk;
62       struct list_user user;
63 
64       if (LMAPI->user_find_list(LMAPI->get_string("list"),LMAPI->get_string("realsender"),
65          &user)) {
66          if (LMAPI->user_hasflag(&user,"ADMIN")) {
67             if (LMAPI->get_bool("admin-approvepost")) {
68                LMAPI->set_var("moderated-approved-by",LMAPI->get_string("realsender"),VAR_GLOBAL);
69             }
70          }
71          if (LMAPI->user_hasflag(&user,"PREAPPROVE")) {
72             LMAPI->set_var("moderated-approved-by",LMAPI->get_string("realsender"),VAR_GLOBAL);
73          }
74          if(LMAPI->user_hasflag(&user, "MODERATOR")) {
75             if (LMAPI->get_bool("moderator-approvepost")) {
76                LMAPI->set_var("moderated-approved-by",LMAPI->get_string("realsender"),VAR_GLOBAL);
77             }
78          }
79       }
80 
81       tempchk = LMAPI->get_var("moderated-approved-by");
82       if (!tempchk) {
83          LMAPI->make_moderated_post("Post to moderated list.");
84          return HOOK_RESULT_STOP;
85       }
86    }
87    return (HOOK_RESULT_OK);
88 }
89 
HOOK_HANDLER(hook_presend_check_modpost)90 HOOK_HANDLER(hook_presend_check_modpost)
91 {
92    /* No point in checking if the list is moderated, eh? */
93    if (!LMAPI->get_bool("moderated")) {
94       const char *tempchk;
95       struct list_user user;
96 
97       if (LMAPI->user_find_list(LMAPI->get_string("list"),LMAPI->get_string("realsender"),
98          &user)) {
99          if (LMAPI->user_hasflag(&user,"MODPOST")) {
100             tempchk = LMAPI->get_var("moderated-approved-by");
101             if (!tempchk) {
102                LMAPI->make_moderated_post("Post by a user set MODPOST.");
103                return HOOK_RESULT_STOP;
104             }
105          }
106       }
107 
108    }
109    return (HOOK_RESULT_OK);
110 }
111 
112 
HOOK_HANDLER(hook_presend_check_closed)113 HOOK_HANDLER(hook_presend_check_closed)
114 {
115    if (LMAPI->get_var("moderated-approved-by")) return HOOK_RESULT_OK;
116 
117    if (LMAPI->get_bool("closed-post")) {
118       struct list_user user;
119       int founduser;
120       char userfilepath[BIG_BUF];  /* Changed from SMALL_BUF to BIG_BUF due to listdir_file */
121 
122       LMAPI->listdir_file(userfilepath, LMAPI->get_string("list"), "users");
123 
124       founduser = LMAPI->user_find_list(LMAPI->get_string("list"),LMAPI->get_string("fromaddress"),&user);
125 
126       if (!founduser) {
127          founduser = LMAPI->user_find_list(LMAPI->get_string("list"),
128                 LMAPI->get_string("fromaddress"),&user);
129       }
130 
131       if (!founduser && LMAPI->get_bool("posting-acl")) {
132          char aclfilename[BIG_BUF];
133          FILE *aclfile;
134          char aclbuffer[BIG_BUF];
135 
136          LMAPI->listdir_file(aclfilename,LMAPI->get_string("list"),
137                              LMAPI->get_string("posting-acl-file"));
138 
139          if (LMAPI->exists_file(aclfilename)) {
140 
141             if ((aclfile = LMAPI->open_file(aclfilename,"r")) != NULL) {
142 
143                while (LMAPI->read_file(aclbuffer, sizeof(aclbuffer), aclfile)) {
144 
145                   if (aclbuffer[strlen(aclbuffer) - 1] == '\n')
146                       aclbuffer[strlen(aclbuffer) - 1] = 0;
147 
148                   if (LMAPI->match_reg(aclbuffer,
149                                        LMAPI->get_string("fromaddress")))
150                      founduser = 1;
151 
152                }
153 
154                LMAPI->close_file(aclfile);
155 
156             }
157 
158          }
159 
160       }
161 
162       if (!founduser && LMAPI->get_var("union-lists")) {
163          char unions[64];
164          char *tptr, *tptr2;
165 
166          LMAPI->log_printf(9,"Checking union lists...\n");
167 
168          LMAPI->buffer_printf(unions, 64, "%.63s", LMAPI->get_var("union-lists"));
169 
170          tptr = &unions[0];
171          tptr2 = strchr(tptr,':');
172          if (tptr2) *tptr2++ = 0;
173 
174          while (!founduser && tptr) {
175             LMAPI->log_printf(9,"Checking '%s'...\n", tptr);
176 
177             founduser = LMAPI->user_find_list(tptr,
178                LMAPI->get_string("fromaddress"), &user);
179 
180             tptr = tptr2;
181 
182             if (tptr) {
183                tptr2 = strchr(tptr,':');
184                if (tptr2) *tptr2++ = 0;
185             }
186          }
187       }
188 
189       if (!founduser) {
190             const char *closedfile;
191             int usedefault;
192             char inbuffer[BIG_BUF];
193             const char *closedsubject;
194 
195             closedfile = LMAPI->get_var("closed-file");
196             usedefault = 1;
197             closedsubject = LMAPI->get_var("closed-post-subject");
198 
199             if (!closedsubject) {
200                LMAPI->buffer_printf(inbuffer, sizeof(inbuffer) - 1, "List '%s' closed to public posts", LMAPI->get_string("list"));
201             }
202             else {
203                LMAPI->buffer_printf(inbuffer, sizeof(inbuffer) - 1, "%s", closedsubject);
204             }
205             LMAPI->set_var("task-form-subject",&inbuffer[0],VAR_TEMP);
206 
207             if (closedfile) {
208                 LMAPI->listdir_file(inbuffer, LMAPI->get_string("list"),
209                                     closedfile);
210 
211                 usedefault = !LMAPI->send_textfile_expand_append(LMAPI->get_string("fromaddress"),&inbuffer[0],LMAPI->get_bool("moderate-include-queue"));
212             }
213 
214             if(!LMAPI->get_bool("closed-post-blackhole")) {
215                 LMAPI->clean_var("moderate-quiet",VAR_TEMP);
216                 if (!usedefault)
217                    LMAPI->set_var("moderate-quiet","yes",VAR_TEMP);
218                 LMAPI->make_moderated_post("Non-member submission to closed-post list.");
219                 LMAPI->clean_var("moderate-quiet",VAR_TEMP);
220             }
221             return HOOK_RESULT_STOP;
222         }
223     }
224     return (HOOK_RESULT_OK);
225 }
226 
HOOK_HANDLER(hook_presend_check_outside)227 HOOK_HANDLER(hook_presend_check_outside)
228 {
229    struct list_user user;
230    int founduser;
231    char userfilepath[BIG_BUF]; /* Changed from SMALL_BUF to BIG_BUF due to listdir_file */
232 
233    if (LMAPI->get_var("moderated-approved-by")) return HOOK_RESULT_OK;
234    if (LMAPI->get_bool("closed-post")) return HOOK_RESULT_OK;
235 
236    LMAPI->listdir_file(userfilepath, LMAPI->get_string("list"), "users");
237 
238    founduser = LMAPI->user_find_list(LMAPI->get_string("list"),LMAPI->get_string("fromaddress"),&user);
239 
240    if (!founduser && LMAPI->get_var("union-lists")) {
241       char unions[64];
242       char *tptr, *tptr2;
243 
244       LMAPI->log_printf(9,"Checking union lists...\n");
245 
246       LMAPI->buffer_printf(unions, 64, "%.63s", LMAPI->get_var("union-lists"));
247 
248       tptr = &unions[0];
249       tptr2 = strchr(tptr,':');
250       if (tptr2) *tptr2++ = 0;
251 
252       while (!founduser && tptr) {
253          LMAPI->log_printf(9,"Checking '%s'...\n", tptr);
254 
255          founduser = LMAPI->user_find_list(tptr,
256                LMAPI->get_string("fromaddress"), &user);
257 
258          tptr = tptr2;
259 
260          if (tptr) {
261             tptr2 = strchr(unions,':');
262             if (tptr2) *tptr2++ = 0;
263          }
264       }
265    }
266 
267    if (!founduser) {
268          const char *outsidefile;
269          char inbuffer[BIG_BUF];
270 
271          outsidefile = LMAPI->get_var("outside-file");
272 
273          if (outsidefile) {
274             LMAPI->buffer_printf(inbuffer, sizeof(inbuffer) - 1, "Re: post to '%s'",
275                LMAPI->get_string("list"));
276 
277             LMAPI->set_var("task-form-subject",&inbuffer[0],VAR_TEMP);
278 
279             LMAPI->listdir_file(inbuffer, LMAPI->get_string("list"),
280                                 outsidefile);
281 
282             LMAPI->send_textfile_expand(LMAPI->get_string("fromaddress"),
283                &inbuffer[0]);
284          }
285          LMAPI->clean_var("task-form-subject",VAR_TEMP);
286     }
287     return (HOOK_RESULT_OK);
288 }
289 
290 
HOOK_HANDLER(hook_presend_check_nopost)291 HOOK_HANDLER(hook_presend_check_nopost)
292 {
293       struct list_user user;
294       int founduser;
295 
296       if (LMAPI->get_var("moderated-approved-by")) return HOOK_RESULT_OK;
297 
298       founduser = LMAPI->user_find_list(LMAPI->get_string("list"),LMAPI->get_string("fromaddress"),&user);
299 
300       if (founduser && LMAPI->user_hasflag(&user,"NOPOST")) {
301             const char *closedfile;
302             int usedefault;
303             char inbuffer[BIG_BUF];
304 
305             closedfile = LMAPI->get_var("nopost-file");
306             usedefault = 1;
307 
308             LMAPI->buffer_printf(inbuffer, sizeof(inbuffer) - 1, "Set NOPOST on list '%s'", LMAPI->get_string("list"));
309             LMAPI->set_var("task-form-subject",&inbuffer[0],VAR_TEMP);
310 
311             if (closedfile) {
312                 LMAPI->listdir_file(inbuffer, LMAPI->get_string("list"),
313                                     closedfile);
314 
315                 usedefault = !LMAPI->send_textfile_expand(LMAPI->get_string("fromaddress"),&inbuffer[0]);
316             }
317 
318             if (usedefault) {
319                 if(!LMAPI->task_heading(LMAPI->get_string("fromaddress")))
320                     return HOOK_RESULT_FAIL;
321                 LMAPI->smtp_body_text("Your post to list '");
322                 LMAPI->smtp_body_text(LMAPI->get_string("list"));
323                 LMAPI->smtp_body_line("' could not be processed.");
324                 LMAPI->smtp_body_line("");
325                 LMAPI->smtp_body_line("You are set NOPOST for this list.  Users who are set NOPOST cannot");
326                 LMAPI->smtp_body_line("post to the list.  This flag is usually used for disciplinary reasons.");
327                 LMAPI->smtp_body_line("");
328                 LMAPI->smtp_body_line("Your post has been eaten.  It was crunchy, and tasted good with");
329                 LMAPI->smtp_body_line("ketchup.");
330                 LMAPI->smtp_body_line("");
331                 LMAPI->smtp_body_line("You may wish to contact a list owner if you are unsure why you are");
332                 LMAPI->smtp_body_line("set NOPOST.");
333                 LMAPI->task_ending();
334             }
335             return HOOK_RESULT_STOP;
336         }
337 
338         return (HOOK_RESULT_OK);
339 }
340 
341 
HOOK_HANDLER(hook_final_send)342 HOOK_HANDLER(hook_final_send)
343 {
344     const char *sendas;
345 
346     /* First thing, build the TO list so that we can know where we're going */
347     if(LMAPI->do_hooks("TOLIST") == HOOK_RESULT_FAIL)
348         return HOOK_RESULT_FAIL;
349 
350     sendas = LMAPI->get_var("send-as");
351     if (!sendas) sendas = LMAPI->get_var("list-owner");
352     if (!sendas) sendas = LMAPI->get_var("listserver-admin");
353 
354     if(!LMAPI->send_to_tolist(sendas, LMAPI->get_string("queuefile"), 1, 1,
355                               LMAPI->get_bool("full-bounce"))) {
356           return HOOK_RESULT_FAIL;
357     }
358 
359     if(LMAPI->do_hooks("AFTER") == HOOK_RESULT_FAIL)
360         return HOOK_RESULT_FAIL;
361     return HOOK_RESULT_OK;
362 }
363 
HOOK_HANDLER(hook_presend_check_messageid)364 HOOK_HANDLER(hook_presend_check_messageid)
365 {
366     char buffer[BIG_BUF], buffer2[BIG_BUF];
367     FILE *infile, *messagefile;
368     time_t lasttime;
369     int inbody, gotit;
370     int docheck;
371 
372     if (!LMAPI->get_bool("no-dupes"))
373         return HOOK_RESULT_OK;
374 
375     docheck = 1;
376 
377     /* If this has been approved, we don't need to dupecheck it.  If an admin/moderator
378        has a mailer loop, they shouldn't be moderating anyway. */
379     if (LMAPI->get_var("moderated-approved-by"))
380        return HOOK_RESULT_OK;
381 
382     infile = LMAPI->open_file(LMAPI->get_string("queuefile"), "r");
383     if (infile == NULL) {
384        return HOOK_RESULT_OK;
385     }
386 
387     LMAPI->listdir_file(buffer, LMAPI->get_string("list"), "nodupes");
388 
389     if ((messagefile = LMAPI->open_exclusive(buffer,"a+")) == NULL) {
390        LMAPI->close_file(infile);
391        return HOOK_RESULT_OK;
392     }
393     LMAPI->rewind_file(messagefile);
394 
395     if(!LMAPI->read_file(buffer, sizeof(buffer), messagefile)) {
396        time(&lasttime);
397        LMAPI->write_file(messagefile,"%d\n",(int)lasttime);
398        docheck = 0;
399     } else {
400        time_t now;
401 
402        time(&now);
403        lasttime = (time_t)atoi(buffer);
404 
405        if (!LMAPI->get_bool("no-dupes-forever")) {
406           if ((now / 86400) != (lasttime / 86400)) {
407              LMAPI->rewind_file(messagefile);
408              LMAPI->truncate_file(messagefile, 0);
409              LMAPI->write_file(messagefile,"%d\n",(int)now);
410              docheck = 0;
411           }
412        }
413     }
414 
415     inbody = 0; gotit = 0;
416 
417     while(LMAPI->read_file(buffer, sizeof(buffer), infile) && !inbody) {
418        if (!strncasecmp(buffer,"Message-Id:",11)) {
419           gotit = 1;
420           break;
421        }
422        if (buffer[0] == '\n') inbody = 1;
423     }
424 
425 
426     if (!gotit) {
427        LMAPI->log_printf(1,"No message ID for message, unable to dupe-check.\n");
428        LMAPI->close_file(messagefile);
429        LMAPI->close_file(infile);
430        return HOOK_RESULT_OK;
431     }
432 
433     if (!docheck) {
434        LMAPI->write_file(messagefile,"%s",buffer);
435        LMAPI->close_file(messagefile);
436        LMAPI->close_file(infile);
437        return HOOK_RESULT_OK;
438     }
439 
440     gotit = 0;
441 
442     while(LMAPI->read_file(buffer2, sizeof(buffer2), messagefile)) {
443        if (!strcasecmp(buffer2,buffer))
444            gotit = 1;
445     }
446 
447     if (!gotit) {
448       /* We should now be situated at the end of the file */
449       LMAPI->write_file(messagefile,"%s",buffer);
450       LMAPI->close_file(messagefile);
451       LMAPI->close_file(infile);
452       return HOOK_RESULT_OK;
453     } else {
454       LMAPI->log_printf(0,"Received duplicate message, ignoring.\n");
455       LMAPI->close_file(infile);
456       return HOOK_RESULT_STOP;
457     }
458 }
459 
HOOK_HANDLER(hook_presend_check_password)460 HOOK_HANDLER(hook_presend_check_password)
461 {
462     char buffer[BIG_BUF], outfilename[BIG_BUF];
463     FILE *infile, *outfile;
464     int gotpass;
465 
466     LMAPI->log_printf(9,"In the password check...\n");
467 
468     if (!LMAPI->get_var("post-password")) return HOOK_RESULT_OK;
469 
470     gotpass = 0;
471 
472     LMAPI->buffer_printf(outfilename, sizeof(outfilename) - 1, "%s.nukepass", LMAPI->get_string("queuefile"));
473 
474     if (!(infile = LMAPI->open_file(LMAPI->get_string("queuefile"),"r"))) {
475        LMAPI->filesys_error(LMAPI->get_string("queuefile"));
476        return HOOK_RESULT_FAIL;
477     }
478 
479     if (!(outfile = LMAPI->open_file(outfilename,"w"))) {
480        LMAPI->close_file(infile);
481        LMAPI->filesys_error(outfilename);
482        return HOOK_RESULT_FAIL;
483     }
484 
485     while(LMAPI->read_file(buffer, sizeof(buffer), infile)) {
486        if (!strncasecmp(buffer,"X-posting-pass:",15) ||
487            !strncasecmp(buffer,"Password:",9)) {
488           const char *pass;
489           char *compare;
490 
491           pass = LMAPI->get_string("post-password");
492 
493           LMAPI->log_printf(5,"Checking posting password...\n");
494 
495           if (!strncasecmp(buffer,"X-posting-pass:",15))
496              compare = &buffer[16];
497           else /* 'Password:' */
498              compare = &buffer[10];
499 
500           if (strncmp(compare,pass,strlen(pass))) {
501              buffer[strlen(buffer) - 1] = 0;
502              LMAPI->log_printf(1, "Invalid password '%s' for list '%s'\n",
503                     &buffer[15], LMAPI->get_string("list"));
504              LMAPI->close_file(outfile);
505              LMAPI->close_file(infile);
506              LMAPI->unlink_file(outfilename);
507              if (LMAPI->get_bool("password-failure-blackhole")) {
508                 LMAPI->result_printf("Invalid password for posting to list '%s'.\n",
509                     LMAPI->get_string("list"));
510              } else {
511                 LMAPI->make_moderated_post("Incorrect password on password-restricted list.");
512              }
513              return HOOK_RESULT_STOP;
514           } else {
515              gotpass = 1;
516           }
517        } else {
518           LMAPI->write_file(outfile,"%s",buffer);
519        }
520     }
521     LMAPI->close_file(infile);
522     LMAPI->close_file(outfile);
523 
524     if (!gotpass) {
525         const char *nopassfile = LMAPI->get_var("post-password-reject-file");
526         int usedefault = 1;
527 
528         if (LMAPI->get_bool("password-failure-blackhole")) {
529            if (nopassfile) {
530                char inbuffer[BIG_BUF];
531                LMAPI->set_var("task-form-subject", "Posting rejected.", VAR_TEMP);
532                LMAPI->listdir_file(inbuffer, LMAPI->get_string("list"),
533                                    nopassfile);
534                usedefault = !LMAPI->send_textfile_expand(LMAPI->get_string("fromaddress"),inbuffer);
535                LMAPI->clean_var("task-form-subject", VAR_TEMP);
536            }
537 
538            if (usedefault) {
539                LMAPI->result_printf("Sorry, you did not provide an X-posting-pass ");
540                LMAPI->result_printf("for list '%s'.\n", LMAPI->get_string("list"));
541            }
542 
543            LMAPI->unlink_file(outfilename);
544         } else {
545            LMAPI->make_moderated_post("Post submitted to moderated list.");
546         }
547         return HOOK_RESULT_STOP;
548     } else {
549        LMAPI->log_printf(3,"Posting validated for list '%s'.\n",
550            LMAPI->get_string("list"));
551        LMAPI->replace_file(outfilename,LMAPI->get_string("queuefile"));
552        if (LMAPI->get_bool("password-implies-approved"))
553           LMAPI->set_var("moderated-approved-by",
554             LMAPI->get_string("realsender"), VAR_GLOBAL);
555        return HOOK_RESULT_OK;
556     }
557 }
558 
HOOK_HANDLER(hook_presend_check_overquoting)559 HOOK_HANDLER(hook_presend_check_overquoting)
560 {
561     int quotepercent, quotelines;
562     int totallines;
563     int quotedlines;
564     int realquotedlines, tolerancelines;
565     int inbody, resetlines;
566     char buffer[BIG_BUF];
567     FILE *infile;
568 
569     LMAPI->log_printf(9,"Overquoting check...\n");
570 
571     if (!LMAPI->get_bool("quoting-limits"))
572        return HOOK_RESULT_OK;
573     if (LMAPI->get_var("moderated-approved-by"))
574        return HOOK_RESULT_OK;
575 
576     quotepercent = LMAPI->get_number("quoting-max-percent");
577     quotelines = LMAPI->get_number("quoting-max-lines");
578     tolerancelines = LMAPI->get_number("quoting-tolerance-lines");
579 
580     /* We can't do anything if they didn't set limits */
581     if (!quotepercent && !quotelines)
582        return HOOK_RESULT_OK;
583 
584     resetlines = LMAPI->get_bool("quoting-line-reset");
585 
586     if ((infile = LMAPI->open_file(LMAPI->get_string("queuefile"),"r")) ==
587         NULL) {
588        LMAPI->filesys_error(LMAPI->get_string("queuefile"));
589        return HOOK_RESULT_FAIL;
590     }
591 
592     totallines = 0; quotedlines = 0; realquotedlines = 0;
593 
594     inbody = 0;
595 
596     while(LMAPI->read_file(buffer, sizeof(buffer), infile)) {
597        if (inbody) totallines++;
598        if (buffer[0] == '\n') inbody = 1;
599        if (buffer[0] == '>') {
600           /* We don't count sendmail escaping against them */
601           if (strncmp(&buffer[1],"From ",5)) {
602              quotedlines++;
603              realquotedlines++;
604           }
605        } else
606        if (buffer[0] == '|') {
607           quotedlines++;
608           realquotedlines++;
609        } else
610        if (resetlines && quotelines) {
611           /* If we reset after a block, AND if we haven't exceeded our
612              maximum already... */
613           if (quotedlines <= quotelines)
614              quotedlines = 0;
615        }
616     }
617 
618     LMAPI->close_file(infile);
619 
620     LMAPI->log_printf(9,"Overquoting: %d %d %d (%d or %d%% max)\n",
621        totallines, realquotedlines, quotedlines, quotelines,
622        quotepercent);
623 
624     if (quotepercent && (tolerancelines ? totallines > tolerancelines : 1)) {
625        int percent;
626 
627        percent = (realquotedlines * 100) / totallines;
628        LMAPI->log_printf(9,"Overquoting: %d percent (%d%% max)\n",
629          percent, quotepercent);
630 
631        if (percent > quotepercent) {
632           char failure[BIG_BUF];
633           char overquotefile[BIG_BUF];
634 
635           LMAPI->buffer_printf(failure, sizeof(failure) - 1,
636             "%d%% of message quoted (%d%% max allowed)",
637                percent, quotepercent);
638 
639           LMAPI->buffer_printf(buffer, sizeof(buffer) - 1, "Overquoting: %s",
640                failure);
641 
642           LMAPI->set_var("overquoting-reason",failure,VAR_TEMP);
643           LMAPI->listdir_file(overquotefile,LMAPI->get_string("list"),
644              LMAPI->get_string("overquote-file"));
645 
646           /* We want the user to know their post was held because of
647              overquoting. */
648           LMAPI->set_var("moderate-force-notify","yes",VAR_TEMP);
649 
650           if (LMAPI->send_textfile_expand_append(
651 					  LMAPI->get_string("realsender"), overquotefile,
652 					  LMAPI->get_bool("overquoting-include-queue")))
653              LMAPI->set_var("moderate-quiet","yes",VAR_TEMP);
654           LMAPI->make_moderated_post(buffer);
655           LMAPI->clean_var("moderate-quiet",VAR_TEMP);
656           LMAPI->clean_var("overquoting-reason",VAR_TEMP);
657           LMAPI->clean_var("moderate-force-notify",VAR_TEMP);
658           return HOOK_RESULT_STOP;
659        }
660     }
661 
662     if (quotelines) {
663        if (quotedlines > quotelines) {
664           char failure[BIG_BUF];
665           char overquotefile[BIG_BUF];
666 
667           LMAPI->buffer_printf(failure, sizeof(failure) - 1,
668             "%d lines quoted%s (%d max allowed)",
669                quotedlines, resetlines ? " in a block" : "", quotelines);
670 
671           LMAPI->buffer_printf(buffer, sizeof(buffer) - 1, "Overquoting: %s",
672                failure);
673 
674           /* We want the user to know their post was held because of
675              overquoting. */
676           LMAPI->set_var("moderate-force-notify","yes",VAR_TEMP);
677 
678           LMAPI->set_var("overquoting-reason",failure,VAR_TEMP);
679           LMAPI->listdir_file(overquotefile,LMAPI->get_string("list"),
680              LMAPI->get_string("overquote-file"));
681           if (LMAPI->send_textfile_expand_append(
682 					  LMAPI->get_string("realsender"), overquotefile,
683 					  LMAPI->get_bool("overquoting-include-queue")))
684              LMAPI->set_var("moderate-quiet","yes",VAR_TEMP);
685           LMAPI->make_moderated_post(buffer);
686           LMAPI->clean_var("moderate-quiet",VAR_TEMP);
687           LMAPI->clean_var("overquoting-reason",VAR_TEMP);
688           LMAPI->clean_var("moderate-force-notify",VAR_TEMP);
689           return HOOK_RESULT_STOP;
690        }
691     }
692 
693     return HOOK_RESULT_OK;
694 }
695 
HOOK_HANDLER(hook_presend_check_bcc)696 HOOK_HANDLER(hook_presend_check_bcc)
697 {
698     FILE *queuefile;
699     char inputbuf[BIG_BUF];
700     int  lastvalid;
701     int  gotaddress;
702     const char *enforceto;
703 
704     enforceto = LMAPI->get_var("enforced-addressing-to");
705 
706     if (!enforceto)
707        return HOOK_RESULT_OK;
708     if (LMAPI->get_var("moderated-approved-by"))
709        return HOOK_RESULT_OK;
710 
711     queuefile = LMAPI->open_file(LMAPI->get_string("queuefile"),"r");
712 
713     if (queuefile == NULL) {
714        LMAPI->filesys_error(LMAPI->get_string("queuefile"));
715        return HOOK_RESULT_FAIL;
716     }
717 
718     gotaddress = 0;
719     lastvalid = 0;
720 
721     while(LMAPI->read_file(inputbuf, sizeof(inputbuf), queuefile) && !gotaddress) {
722        if (inputbuf[0] == '\n') break;
723 
724        if (isspace((int)(inputbuf[0])) && lastvalid) {
725           if (LMAPI->strcasestr(inputbuf, enforceto)) gotaddress = 1;
726        } else lastvalid = 0;
727 
728        if (strncasecmp(inputbuf,"To:",3) == 0) {
729           lastvalid = 1;
730           if (LMAPI->strcasestr(inputbuf, enforceto)) gotaddress = 1;
731        }
732        else if (strncasecmp(inputbuf,"Cc:",3) == 0) {
733           lastvalid = 1;
734           if (LMAPI->strcasestr(inputbuf, enforceto)) gotaddress = 1;
735        }
736     }
737 
738     LMAPI->close_file(queuefile);
739 
740     if (gotaddress) return HOOK_RESULT_OK;
741 
742     if (LMAPI->get_var("enforced-address-blackhole"))
743        return HOOK_RESULT_STOP;
744 
745     LMAPI->make_moderated_post("The list address was not in the To or Cc fields.  This list does not\naccept Bcc'd posts.");
746 
747     return HOOK_RESULT_STOP;
748 }
749 
750