1 /*
2 * ProFTPD: mod_ratio -- Support upload/download ratios.
3 * Portions Copyright (c) 1998-1999 Johnie Ingram.
4 * Copyright (c) 2002 James Dogopoulos.
5 * Copyright (c) 2008-2017 The ProFTPD Project team
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
13 * and other respective copyright holders give permission to link this program
14 * with OpenSSL, and distribute the resulting executable, without including
15 * the source code for OpenSSL in the source distribution.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
25 */
26
27 #define MOD_RATIO_VERSION "mod_ratio/3.3"
28
29 /* This is mod_ratio, contrib software for proftpd 1.2.0 and above.
30 For more information contact James Dogopoulos <jd@dynw.com> or
31 Johnie Ingram <johnie@netgod.net>.
32
33 History Log:
34
35 * 2002-05-24: v3.3: Fixed numerous bugs and compatibility issues with
36 changing code within ProFTPD. In other words, it /works/ again!
37 Added default AnonRatio support (AnonRatio * ...).
38
39 * 2000-08-01: v3.2: Fixed CwdRatio directive. Ratios are no longer part
40 of CWD or NOOP commands. Fixed file byte ratio printing. It is now
41 UL:DL - the way it should be. Updated README.ratio file. - jD
42
43 * 2000-02-15: v3.1: Added support to save user stats to plain text file.
44 (See README.ratio) Changed display to MBytes rather than Bytes. Tracks
45 to the nearest Kilobyte rather than byte. - jD
46
47 * 1999-10-03: v3.0: Uses generic API to access SQL data at runtime.
48 Supports negative ratios (upload X to get 1) by popular demand.
49 Added proper SITE command and help. Various presentation
50 idiosyncracies fixed.
51
52 * 1999-06-13: v2.2: fixed ratio display, it was printing ratios in
53 reverse order.
54
55 * 1999-05-03: v2.1: mod_mysql bugfix; rearranged CWD reply so
56 Netscape shows ratios; fixed recalculation in XRATIO. Added
57 CwdRatioMsg directive for showing equivalent URLs (always
58 enabled).
59
60 * 1999-04-08: v2.0: Reformat and rewrite. Add FileRatioErrMsg,
61 ByteRatioErrMsg, and LeechRatioMsg directives and support for
62 proftpd mod_mysql.
63
64 * 1998-09-08: v1.0: Accepted into CVS as a contrib module.
65
66 */
67
68 #include "conf.h"
69
70 int gotratuser,fileerr;
71
72 static struct
73 {
74 int fstor, fretr, frate, fcred, brate, bcred;
75 int files;
76
77 off_t bstor, bretr;
78 off_t bytes;
79
80 char ftext [64],btext [64];
81
82 } stats;
83
84 static struct
85 {
86 int enable;
87 int save;
88 char user [PR_TUNABLE_LOGIN_MAX];
89
90 const char *rtype; /* The ratio type currently in effect. */
91
92 const char *filemsg;
93 const char *bytemsg;
94 const char *leechmsg;
95 const char *ratiofile;
96 const char *ratiotmp;
97 } g;
98
99 #define RATIO_ENFORCE (stats.frate || stats.brate)
100
101 #define RATIO_STUFFS "-%d/%lu +%d/%lu (%d %d %d %d) = %d/%lu%s%s", \
102 stats.fretr, (unsigned long) (stats.bretr / 1024), \
103 stats.fstor, (unsigned long) (stats.bstor / 1024), \
104 stats.frate, stats.fcred, stats.brate, stats.bcred, \
105 stats.files, (unsigned long) (stats.bytes / 1024), \
106 (stats.frate && stats.files < 1) ? " [NO F]" : "", \
107 (stats.brate && (stats.bytes / 1024) < 5) ? " [LO B]" : ""
108 #define SHORT_RATIO_STUFFS "-%d/%lu +%d/%lu = %d/%lu%s%s", \
109 stats.fretr, (unsigned long) (stats.bretr / 1024), \
110 stats.fstor, (unsigned long) (stats.bstor / 1024), \
111 stats.files, (unsigned long) (stats.bytes / 1024), \
112 (stats.frate && stats.files < 1) ? " [NO F]" : "", \
113 (stats.brate && (stats.bytes / 1024) < 5) ? " [LO B]" : ""
114
115 static cmd_rec *
_make_cmd(pool * cp,int argc,...)116 _make_cmd (pool * cp, int argc, ...)
117 {
118 va_list args;
119 cmd_rec *c;
120 int i;
121
122 pool *newpool = NULL;
123 newpool = make_sub_pool( cp );
124 c = pcalloc(newpool, sizeof(cmd_rec));
125
126 c->pool = newpool;
127 c->argv = pcalloc(newpool, sizeof(void *) * (argc + 1));
128
129 c->argc = argc;
130 c->stash_index = -1;
131
132 c->argv[0] = MOD_RATIO_VERSION;
133 va_start (args, argc);
134 for (i = 0; i < argc; i++)
135 c->argv[i + 1] = (void *) va_arg (args, char *);
136 va_end (args);
137
138 return c;
139 }
140
141 static modret_t *
_dispatch_ratio(cmd_rec * cmd,char * match)142 _dispatch_ratio (cmd_rec * cmd, char *match)
143 {
144 authtable *m;
145 modret_t *mr = NULL;
146
147 m = pr_stash_get_symbol2 (PR_SYM_AUTH, match, NULL, &cmd->stash_index,
148 &cmd->stash_hash);
149 while (m)
150 {
151 mr = pr_module_call (m->m, m->handler, cmd);
152 if (MODRET_ISHANDLED (mr) || MODRET_ISERROR (mr))
153 break;
154 m = pr_stash_get_symbol2 (PR_SYM_AUTH, match, m, &cmd->stash_index,
155 &cmd->stash_hash);
156 }
157
158 if (MODRET_ISERROR(mr))
159 pr_log_debug(DEBUG0, MOD_RATIO_VERSION ": internal error: %s",
160 MODRET_ERRMSG(mr));
161
162 return mr;
163 }
164
165 static modret_t *
_dispatch(cmd_rec * cmd,char * match)166 _dispatch (cmd_rec * cmd, char *match)
167 {
168 cmd_rec *cr;
169 modret_t *mr = 0;
170
171 cr = _make_cmd (cmd->tmp_pool, 0);
172 mr = _dispatch_ratio (cr, match);
173 if (cr->tmp_pool)
174 destroy_pool (cr->tmp_pool);
175 return mr;
176 }
177
178 static void
set_stats(const char * fstor,const char * fretr,const char * bstor,const char * bretr)179 set_stats (const char *fstor, const char *fretr, const char *bstor,
180 const char *bretr)
181 {
182
183 if (fstor)
184 stats.fstor = atoi(fstor);
185
186 if (fretr)
187 stats.fretr = atoi(fretr);
188
189 #ifdef HAVE_STRTOULL
190 if (bstor) {
191 char *tmp = NULL;
192 off_t res;
193
194 res = strtoull(bstor, &tmp, 10);
195 if (tmp == NULL)
196 stats.bstor = res;
197 }
198
199 if (bretr) {
200 char *tmp = NULL;
201 off_t res;
202
203 res = strtoull(bretr, &tmp, 10);
204 if (tmp == NULL)
205 stats.bretr = res;
206 }
207 #else
208 if (bstor) {
209 char *tmp = NULL;
210 off_t res;
211
212 res = strtoul(bstor, &tmp, 10);
213 if (tmp == NULL)
214 stats.bstor = res;
215 }
216
217 if (bretr) {
218 char *tmp = NULL;
219 off_t res;
220
221 res = strtoul(bretr, &tmp, 10);
222 if (tmp == NULL)
223 stats.bretr = res;
224 }
225 #endif /* HAVE_STRTOULL */
226 }
227
update_ratios(const char * frate,const char * fcred,const char * brate,const char * bcred)228 static void update_ratios(const char *frate, const char *fcred,
229 const char *brate, const char *bcred) {
230 stats.frate = stats.fcred = stats.brate = stats.bcred = 0;
231
232 if (frate)
233 stats.frate = atoi (frate);
234 if (fcred)
235 stats.fcred = atoi (fcred);
236 if (brate)
237 stats.brate = atoi (brate);
238 if (bcred)
239 stats.bcred = atoi (bcred);
240
241 if (stats.frate >= 0)
242 {
243 stats.files = (stats.frate * stats.fstor) + stats.fcred - stats.fretr;
244 memset(stats.ftext, '\0', sizeof(stats.ftext));
245 pr_snprintf (stats.ftext, sizeof(stats.ftext)-1, "1:%dF", stats.frate);
246 }
247 else
248 {
249 stats.files = (stats.fstor / (stats.frate * -1)) + stats.fcred - stats.fretr;
250 memset(stats.ftext, '\0', sizeof(stats.ftext));
251 pr_snprintf (stats.ftext, sizeof(stats.ftext)-1, "%d:1F", stats.frate * -1);
252 }
253
254 if (stats.brate >= 0)
255 {
256 stats.bytes = (stats.brate * stats.bstor) + stats.bcred - stats.bretr;
257 memset(stats.btext, '\0', sizeof(stats.btext));
258 pr_snprintf (stats.btext, sizeof(stats.btext)-1, "1:%dB", stats.brate);
259 }
260 else
261 {
262 stats.bytes = (stats.bstor / (stats.brate * -1)) + stats.bcred - stats.bretr;
263 memset(stats.btext, '\0', sizeof(stats.btext));
264 pr_snprintf (stats.btext, sizeof(stats.btext)-1, "%d:1B", stats.brate * -1);
265 }
266 }
267
268
calc_ratios(cmd_rec * cmd)269 MODRET calc_ratios (cmd_rec * cmd)
270 {
271 modret_t *mr = 0;
272 config_rec *c;
273 char buf[1024] = {'\0'};
274 char *mask;
275 char **data;
276 void *ptr;
277
278 ptr = get_param_ptr (main_server->conf, "Ratios", FALSE);
279 if (ptr)
280 g.enable = *((int *) ptr);
281
282 if (!g.enable)
283 return PR_DECLINED (cmd);
284
285 mr = _dispatch (cmd, "getstats");
286 if (MODRET_HASDATA (mr))
287 {
288 data = mr->data;
289 if (data[4])
290 pr_log_debug(DEBUG4, MOD_RATIO_VERSION
291 ": warning: getstats on %s not unique", g.user);
292 set_stats (data[0], data[1], data[2], data[3]);
293 }
294
295 mr = _dispatch (cmd, "getratio");
296 if (MODRET_HASDATA (mr))
297 {
298 data = mr->data;
299 if (data[4])
300 pr_log_debug(DEBUG4, MOD_RATIO_VERSION
301 ": warning: getratio on %s not unique", g.user);
302 update_ratios(data[0], data[1], data[2], data[3]);
303 g.rtype = "U";
304 return PR_DECLINED (cmd);
305 }
306
307 c = find_config (main_server->conf, CONF_PARAM, "HostRatio", TRUE);
308 while (c)
309 {
310 mask = buf;
311 if (*(char *) c->argv[0] == '.')
312 {
313 *mask++ = '*';
314 sstrncpy (mask, c->argv[0], sizeof (buf));
315 }
316 else if (*(char *) ((char *) c->argv[0] + (strlen (c->argv[0]) - 1)) == '.')
317 {
318 sstrncpy (mask, c->argv[0], sizeof(buf) - 2);
319 sstrcat(buf, "*", sizeof(buf));
320 }
321 else
322 sstrncpy (mask, c->argv[0], sizeof (buf));
323
324 if (!pr_fnmatch (buf, session.c->remote_name, PR_FNM_NOESCAPE | PR_FNM_CASEFOLD) ||
325 !pr_fnmatch (buf, pr_netaddr_get_ipstr (session.c->remote_addr),
326 PR_FNM_NOESCAPE | PR_FNM_CASEFOLD))
327 {
328 update_ratios(c->argv[1], c->argv[2], c->argv[3], c->argv[4]);
329 g.rtype = "h";
330 return PR_DECLINED (cmd);
331 }
332 c = find_config_next (c, c->next, CONF_PARAM, "HostRatio", FALSE);
333 }
334
335 c = find_config (main_server->conf, CONF_PARAM, "AnonRatio", TRUE);
336 while (c)
337 {
338 if ((session.anon_user && !strcmp (c->argv[0], session.anon_user)) ||
339 *(char *) c->argv[0] == '*')
340 {
341 update_ratios(c->argv[1], c->argv[2], c->argv[3], c->argv[4]);
342 g.rtype = "a";
343 return PR_DECLINED (cmd);
344 }
345 c = find_config_next (c, c->next, CONF_PARAM, "AnonRatio", FALSE);
346 }
347
348 c = find_config (main_server->conf, CONF_PARAM, "UserRatio", TRUE);
349 while (c)
350 {
351 if (*(char *) c->argv[0] == '*' || !strcmp (c->argv[0], g.user))
352 {
353 update_ratios(c->argv[1], c->argv[2], c->argv[3], c->argv[4]);
354 g.rtype = "u";
355 return PR_DECLINED (cmd);
356 }
357 c = find_config_next (c, c->next, CONF_PARAM, "UserRatio", FALSE);
358 }
359
360 c = find_config(main_server->conf, CONF_PARAM, "GroupRatio", FALSE);
361 while (c) {
362 pr_signals_handle();
363
364 if (strcmp(c->argv[0], session.group) == 0) {
365 update_ratios(c->argv[1], c->argv[2], c->argv[3], c->argv[4]);
366 g.rtype = "g";
367
368 return PR_DECLINED(cmd);
369
370 } else {
371 if (session.groups) {
372 register unsigned int i;
373 char **group_names = session.groups->elts;
374
375 /* Check the list of supplemental groups for this user as well. */
376 for (i = 0; i < session.groups->nelts-1; i++) {
377 if (strcmp(c->argv[0], group_names[i]) == 0) {
378 update_ratios(c->argv[1], c->argv[2], c->argv[3], c->argv[4]);
379 g.rtype = "g";
380
381 return PR_DECLINED(cmd);
382 }
383 }
384 }
385 }
386
387 c = find_config_next(c, c->next, CONF_PARAM, "GroupRatio", FALSE);
388 }
389
390 return PR_DECLINED (cmd);
391 }
392
393 static void
log_ratios(cmd_rec * cmd)394 log_ratios (cmd_rec * cmd)
395 {
396 char buf[1024] = {'\0'};
397
398 memset(buf, '\0', sizeof(buf));
399 pr_snprintf (buf, sizeof(buf)-1, SHORT_RATIO_STUFFS);
400 pr_log_debug(DEBUG0, MOD_RATIO_VERSION ": %s in %s: %s %s%s%s", g.user,
401 session.cwd, cmd->argv[0], cmd->arg, RATIO_ENFORCE ? " :" : "",
402 RATIO_ENFORCE ? buf : "");
403 }
404
405 static void
update_stats(void)406 update_stats (void)
407 {
408 FILE *usrfile = NULL, *newfile = NULL;
409 char usrstr[256] = {'\0'}, *ratname;
410 int ulfiles,dlfiles,cpc;
411 off_t ulbytes = 0, dlbytes = 0;
412
413 if (!fileerr) {
414 newfile = fopen(g.ratiotmp, "w");
415 if (newfile == NULL) {
416 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
417 ": error opening temporary ratios file '%s': %s", g.ratiotmp,
418 strerror(errno));
419 gotratuser = 1;
420 fileerr = 1;
421 return;
422 }
423 }
424
425 usrfile = fopen(g.ratiofile, "r");
426
427 if (usrfile != NULL) {
428 while (fgets(usrstr, sizeof(usrstr), usrfile) != NULL) {
429 char *tok = NULL;
430
431 pr_signals_handle();
432
433 tok = strtok(usrstr, "|");
434 ratname = tok;
435
436 tok = strtok(NULL, "|");
437 ulfiles = atoi(tok);
438
439 tok = strtok(NULL, "|");
440 if (tok) {
441 char *tmp = NULL;
442 off_t res;
443
444 #ifdef HAVE_STRTOULL
445 res = strtoull(tok, &tmp, 10);
446 #else
447 res = strtoul(tok, &tmp, 10);
448 #endif /* HAVE_STRTOULL */
449
450 if (tmp == NULL)
451 ulbytes = res;
452 }
453
454 tok = strtok(NULL, "|");
455 dlfiles = atoi(tok);
456
457 tok = strtok(NULL, "|");
458 if (tok) {
459 char *tmp = NULL;
460 off_t res;
461
462 #ifdef HAVE_STRTOULL
463 res = strtoull(tok, &tmp, 10);
464 #else
465 res = strtoul(tok, &tmp, 10);
466 #endif /* HAVE_STRTOULL */
467
468 if (tmp == NULL)
469 dlbytes = res;
470 }
471
472 if (strcmp(ratname, g.user) == 0) {
473 fprintf(newfile, "%s|%d|%" PR_LU "|%d|%" PR_LU "\n", g.user,
474 stats.fstor, (pr_off_t) stats.bstor, stats.fretr,
475 (pr_off_t) stats.bretr);
476
477 } else {
478 fprintf(newfile, "%s|%d|%" PR_LU "|%d|%" PR_LU "\n", ratname,
479 ulfiles, (pr_off_t) ulbytes, dlfiles, (pr_off_t) dlbytes);
480 }
481 }
482
483 } else {
484 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
485 ": error opening ratios file '%s': %s", g.ratiofile,
486 strerror(errno));
487 gotratuser = 1;
488 fileerr = 1;
489 }
490
491 if (usrfile)
492 fclose(usrfile);
493
494 if (newfile)
495 fclose(newfile);
496
497 newfile = fopen(g.ratiotmp, "rb");
498 if (newfile == NULL) {
499 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
500 ": error opening temporary ratios file '%s': %s", g.ratiotmp,
501 strerror(errno));
502 }
503
504 usrfile = fopen(g.ratiofile, "wb");
505 if (usrfile == NULL) {
506 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
507 ": error opening ratios file '%s': %s", g.ratiofile,
508 strerror(errno));
509 }
510
511 if (newfile != NULL &&
512 usrfile != NULL) {
513
514 while ((cpc = getc(newfile)) != EOF) {
515 pr_signals_handle();
516 putc(cpc, usrfile);
517 }
518 }
519
520 if (usrfile)
521 fclose(usrfile);
522
523 if (newfile)
524 fclose(newfile);
525 }
526
527 MODRET
pre_cmd_retr(cmd_rec * cmd)528 pre_cmd_retr (cmd_rec * cmd)
529 {
530 char *path;
531 int fsize = 0;
532 struct stat sbuf;
533
534 calc_ratios (cmd);
535 if (!g.enable)
536 return PR_DECLINED (cmd);
537 log_ratios (cmd);
538
539 if (!RATIO_ENFORCE)
540 return PR_DECLINED (cmd);
541
542 if (stats.frate && stats.files < 1)
543 {
544 pr_response_add_err (R_550, "%s", g.filemsg);
545 pr_response_add_err (R_550,
546 "%s: FILE RATIO: %s Down: %d Up: only %d!",
547 cmd->arg, stats.ftext, stats.fretr, stats.fstor);
548 return PR_ERROR (cmd);
549 }
550
551 if (stats.brate)
552 {
553 path = dir_realpath (cmd->tmp_pool, cmd->arg);
554 if (path
555 && dir_check (cmd->tmp_pool, cmd, cmd->group, path, NULL)
556 && pr_fsio_stat (path, &sbuf) > -1)
557 fsize = sbuf.st_size;
558
559 if ((stats.bytes - (fsize / 1024)) < 0)
560 {
561 pr_response_add_err (R_550, "%s", g.bytemsg);
562 pr_response_add_err (R_550,
563 "%s: BYTE RATIO: %s Down: %lumb Up: only %lumb!", cmd->arg,
564 stats.btext, (unsigned long) (stats.bretr / 1024),
565 (unsigned long) (stats.bstor / 1024));
566 return PR_ERROR (cmd);
567 }
568 }
569
570 return PR_DECLINED (cmd);
571 }
572
ratio_log_pass(cmd_rec * cmd)573 MODRET ratio_log_pass(cmd_rec *cmd) {
574 if (session.anon_user) {
575 sstrncpy(g.user, session.anon_user, sizeof(g.user));
576 }
577
578 calc_ratios (cmd);
579 if (g.enable) {
580 char buf[256];
581
582 memset(buf, '\0', sizeof(buf));
583 pr_snprintf(buf, sizeof(buf)-1, RATIO_STUFFS);
584 pr_log_pri(PR_LOG_INFO, "Ratio: %s/%s %s[%s]: %s.", g.user,
585 session.group, session.c->remote_name, pr_netaddr_get_ipstr
586 (session.c->remote_addr), buf);
587 }
588
589 return PR_DECLINED (cmd);
590 }
591
592 MODRET
pre_cmd(cmd_rec * cmd)593 pre_cmd (cmd_rec * cmd)
594 {
595 if (g.enable)
596 {
597 /* if (!strcasecmp (cmd->argv[0], "STOR")) */
598 if (strcasecmp (cmd->argv[0], "STOR") || strcasecmp(cmd->argv[0], "RETR"))
599 calc_ratios (cmd);
600 log_ratios (cmd);
601 }
602 return PR_DECLINED (cmd);
603 }
604
605 MODRET
cmd_cwd(cmd_rec * cmd)606 cmd_cwd (cmd_rec * cmd)
607 {
608 char *dir;
609 config_rec *c = find_config (main_server->conf, CONF_PARAM, "CwdRatioMsg", TRUE);
610 if (c)
611 {
612 dir = dir_realpath (cmd->tmp_pool, cmd->argv[1]);
613 while (dir && c)
614 {
615 if (!*((char *) c->argv[0]))
616 return PR_DECLINED (cmd);
617 pr_response_add (R_250, "%s", (char *) c->argv[0]);
618 c = find_config_next (c, c->next, CONF_PARAM, "CwdRatioMsg", FALSE);
619 }
620 }
621 return PR_DECLINED (cmd);
622 }
623
ratio_post_cmd(cmd_rec * cmd)624 MODRET ratio_post_cmd(cmd_rec *cmd) {
625 FILE *usrfile = NULL, *newfile = NULL;
626 char sbuf1[128] = {'\0'}, sbuf2[128] = {'\0'},
627 sbuf3[128] = {'\0'}, usrstr[256] = {'\0'};
628 char *ratname;
629 int ulfiles,dlfiles,cpc;
630 off_t ulbytes = 0, dlbytes = 0;
631
632 if (!gotratuser && g.save) {
633 usrfile = fopen(g.ratiofile, "r");
634 if (usrfile == NULL) {
635 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
636 ": error opening ratios file '%s': %s", g.ratiofile,
637 strerror(errno));
638 gotratuser = 1;
639 fileerr = 1;
640 }
641 }
642
643 if (session.anon_user)
644 sstrncpy(g.user, session.anon_user, sizeof(g.user));
645
646 if (strlen(g.user) == 0)
647 sstrncpy(g.user, "NOBODY", sizeof(g.user));
648
649 if (!gotratuser && !fileerr && g.save) {
650 if (!usrfile)
651 usrfile = fopen(g.ratiofile, "r");
652
653 if (usrfile) {
654 while (fgets(usrstr, sizeof(usrstr), usrfile) != NULL) {
655 char *tok = NULL;
656
657 pr_signals_handle();
658
659 tok = strtok(usrstr, "|");
660 ratname = tok;
661
662 tok = strtok(NULL, "|");
663 ulfiles = atoi(tok);
664
665 tok = strtok(NULL, "|");
666 if (tok) {
667 char *tmp = NULL;
668 off_t res;
669
670 #ifdef HAVE_STRTOULL
671 res = strtoull(tok, &tmp, 10);
672 #else
673 res = strtoul(tok, &tmp, 10);
674 #endif /* HAVE_STRTOULL */
675
676 if (tmp == NULL)
677 ulbytes = res;
678 }
679
680 tok = strtok(NULL, "|");
681 dlfiles = atoi(tok);
682
683 tok = strtok(NULL, "|");
684 if (tok) {
685 char *tmp = NULL;
686 off_t res;
687
688 #ifdef HAVE_STRTOULL
689 res = strtoull(tok, &tmp, 10);
690 #else
691 res = strtoul(tok, &tmp, 10);
692 #endif /* HAVE_STRTOULL */
693
694 if (tmp == NULL)
695 dlbytes = res;
696 }
697
698 if (strcmp(ratname, g.user) == 0) {
699 stats.fretr += dlfiles;
700 stats.bretr += dlbytes;
701 stats.fstor += ulfiles;
702 stats.bstor += ulbytes;
703 gotratuser = 1;
704 }
705 }
706 fclose(usrfile);
707
708 } else {
709 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
710 ": error opening ratios file '%s': %s", g.ratiofile,
711 strerror(errno));
712 gotratuser = 1;
713 fileerr = 1;
714 }
715
716 /* Entry for user must not exist, create... */
717 if (!gotratuser && !fileerr) {
718 newfile = fopen(g.ratiotmp, "w");
719 if (newfile == NULL) {
720 pr_log_debug(DEBUG3, MOD_RATIO_VERSION
721 ": error opening temporary ratios file '%s': %s",
722 g.ratiotmp, strerror(errno));
723 gotratuser = 1;
724 fileerr = 1;
725 }
726 }
727
728 if (!gotratuser && !fileerr) {
729 usrfile = fopen(g.ratiofile, "r");
730 if (usrfile) {
731
732 /* Copy the existing lines into the temporary file. */
733 while (fgets(usrstr, sizeof(usrstr), usrfile) != NULL) {
734 pr_signals_handle();
735 fprintf(newfile, "%s", usrstr);
736 }
737
738 fprintf(newfile, "%s|%d|%" PR_LU "|%d|%" PR_LU "\n", g.user,
739 stats.fstor, (pr_off_t) stats.bstor, stats.fretr,
740 (pr_off_t) stats.bretr);
741
742 fclose(usrfile);
743 fclose(newfile);
744
745 /* Copy the temporary file to the actual file. */
746 newfile = fopen(g.ratiotmp, "rb");
747 usrfile = fopen(g.ratiofile, "wb");
748
749 if (newfile != NULL &&
750 usrfile != NULL) {
751
752 while ((cpc = getc(newfile)) != EOF) {
753 pr_signals_handle();
754 putc(cpc, usrfile);
755 }
756 }
757
758 if (usrfile)
759 fclose(usrfile);
760
761 if (newfile)
762 fclose(newfile);
763 }
764 }
765 }
766
767 if (g.enable) {
768 int cwding = !strcasecmp (cmd->argv[0], "CWD");
769 char *r = (cwding) ? R_250 : R_DUP;
770 sbuf1[0] = sbuf2[0] = sbuf3[0] = 0;
771 if (cwding || !strcasecmp (cmd->argv[0], "PASS"))
772 calc_ratios (cmd);
773
774 pr_snprintf(sbuf1, sizeof(sbuf1), "Down: %d Files (%lumb) Up: %d Files (%lumb)",
775 stats.fretr, (unsigned long) (stats.bretr / 1024),
776 stats.fstor, (unsigned long) (stats.bstor / 1024));
777 if (stats.frate)
778 pr_snprintf (sbuf2, sizeof(sbuf2),
779 " %s CR: %d", stats.ftext, stats.files);
780 if (stats.brate)
781 pr_snprintf (sbuf3, sizeof(sbuf3), " %s CR: %lu", stats.btext,
782 (unsigned long) (stats.bytes / 1024));
783
784 if (RATIO_ENFORCE)
785 {
786 pr_response_add (r, "%s%s%s", sbuf1, sbuf2, sbuf3);
787 if (stats.frate && stats.files < 0)
788 pr_response_add (r, "%s", g.filemsg);
789 if (stats.brate && stats.bytes < 0)
790 pr_response_add (r, "%s", g.bytemsg);
791 }
792 else
793 pr_response_add (r, "%s%s%s", sbuf1, g.leechmsg ? " " : "", g.leechmsg);
794 }
795
796 return PR_DECLINED(cmd);
797 }
798
799 MODRET
cmd_site(cmd_rec * cmd)800 cmd_site (cmd_rec * cmd)
801 {
802 char buf[128] = {'\0'};
803
804 if (cmd->argc < 2)
805 return PR_DECLINED(cmd);
806
807 if (strcasecmp(cmd->argv[1], "RATIO") == 0) {
808 calc_ratios(cmd);
809 pr_snprintf(buf, sizeof(buf), RATIO_STUFFS);
810 pr_response_add(R_214, "Current Ratio: ( %s )", buf);
811 if(stats.frate)
812 pr_response_add(R_214,
813 "Files: %s Down: %d Up: %d CR: %d file%s",
814 stats.ftext, stats.fretr, stats.fstor,
815 stats.files, (stats.files != 1) ? "s" : "");
816 if(stats.brate)
817 pr_response_add(R_214,
818 "Bytes: %s Down: %lumb Up: %lumb CR: %lu Mbytes",
819 stats.btext, (unsigned long) (stats.bretr / 1024),
820 (unsigned long) (stats.bstor / 1024),
821 (unsigned long) (stats.bytes / 1024));
822 return PR_HANDLED(cmd);
823 }
824
825 if (strcasecmp (cmd->argv[1], "HELP") == 0) {
826 pr_response_add(R_214,
827 "The following SITE extensions are recognized:");
828 pr_response_add(R_214, "RATIO " "-- show all ratios in effect");
829 }
830
831 return PR_DECLINED (cmd);
832 }
833
834 /* FIXME: because of how ratio and sql interact, the status sent after
835 STOR and RETR commands is always out-of-date. Reorder module loading? */
836
ratio_post_retr(cmd_rec * cmd)837 MODRET ratio_post_retr(cmd_rec *cmd) {
838 stats.fretr++;
839 stats.bretr += (session.xfer.total_bytes / 1024);
840
841 calc_ratios (cmd);
842
843 if (!fileerr && g.save) {
844 update_stats ();
845 }
846
847 return ratio_post_cmd(cmd);
848 }
849
ratio_post_stor(cmd_rec * cmd)850 MODRET ratio_post_stor(cmd_rec *cmd) {
851 stats.fstor++;
852 stats.bstor += (session.xfer.total_bytes / 1024);
853
854 calc_ratios (cmd);
855
856 if (!fileerr && g.save) {
857 update_stats ();
858 }
859
860 return ratio_post_cmd(cmd);
861 }
862
863 MODRET
cmd_user(cmd_rec * cmd)864 cmd_user (cmd_rec * cmd)
865 {
866 if (!g.user[0])
867 sstrncpy (g.user, cmd->argv[1], PR_TUNABLE_LOGIN_MAX);
868 return PR_DECLINED (cmd);
869 }
870
871 /* **************************************************************** */
872
873 MODRET
add_ratiodata(cmd_rec * cmd)874 add_ratiodata (cmd_rec * cmd)
875 {
876 CHECK_ARGS (cmd, 5);
877 CHECK_CONF (cmd,
878 CONF_ROOT | CONF_VIRTUAL | CONF_ANON | CONF_DIR | CONF_GLOBAL);
879 add_config_param_str (cmd->argv[0], 5, (void *) cmd->argv[1],
880 (void *) cmd->argv[2], (void *) cmd->argv[3],
881 (void *) cmd->argv[4], (void *) cmd->argv[5]);
882 return PR_HANDLED (cmd);
883 }
884
set_ratios(cmd_rec * cmd)885 MODRET set_ratios(cmd_rec *cmd) {
886 int bool;
887 config_rec *c;
888
889 CHECK_ARGS(cmd, 1);
890 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
891
892 bool = get_boolean(cmd, 1);
893 if (bool == -1) {
894 CONF_ERROR(cmd, "expected Boolean parameter");
895 }
896
897 c = add_config_param(cmd->argv[0], 1, NULL);
898 c->argv[0] = pcalloc(c->pool, sizeof(int));
899 *((int *) c->argv[0]) = bool;
900 c->flags |= CF_MERGEDOWN;
901
902 return PR_HANDLED(cmd);
903 }
904
905 MODRET
add_saveratios(cmd_rec * cmd)906 add_saveratios (cmd_rec * cmd)
907 {
908 int b;
909 config_rec *c;
910
911 CHECK_ARGS (cmd, 1);
912 CHECK_CONF (cmd, CONF_ROOT | CONF_VIRTUAL
913 | CONF_ANON | CONF_DIR | CONF_GLOBAL);
914 b = get_boolean (cmd, 1);
915 if (b == -1)
916 CONF_ERROR (cmd, "requires a boolean value");
917 c = add_config_param (cmd->argv[0], 1, NULL);
918 c->argv[0] = pcalloc(c->pool, sizeof(int));
919 *((int *) c->argv[0]) = b;
920 c->flags |= CF_MERGEDOWN;
921 return PR_HANDLED (cmd);
922 }
923
924 MODRET
add_str(cmd_rec * cmd)925 add_str (cmd_rec * cmd)
926 {
927 CHECK_ARGS (cmd, 1);
928 CHECK_CONF (cmd, CONF_ROOT | CONF_VIRTUAL
929 | CONF_ANON | CONF_DIR | CONF_GLOBAL);
930 add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
931 return PR_HANDLED (cmd);
932 }
933
934 /* **************************************************************** */
935
ratio_sess_init(void)936 static int ratio_sess_init(void) {
937 void *ptr;
938
939 memset(&g, 0, sizeof (g));
940
941 ptr = get_param_ptr(TOPLEVEL_CONF, "Ratios", FALSE);
942 if (ptr) {
943 g.enable = *((int *) ptr);
944 }
945
946 ptr = get_param_ptr (TOPLEVEL_CONF, "SaveRatios", FALSE);
947 if (ptr)
948 g.save = *((int *) ptr);
949
950 if (!(g.filemsg = get_param_ptr (TOPLEVEL_CONF, "FileRatioErrMsg", FALSE)))
951 g.filemsg = "Too few files uploaded to earn file -- please upload more.";
952
953 if (!(g.ratiofile = get_param_ptr (TOPLEVEL_CONF, "RatioFile", FALSE)))
954 g.ratiofile = "";
955
956 if (!(g.ratiotmp = get_param_ptr (TOPLEVEL_CONF, "RatioTempFile", FALSE)))
957 g.ratiotmp = "";
958
959 if (!(g.bytemsg = get_param_ptr (TOPLEVEL_CONF, "ByteRatioErrMsg", FALSE)))
960 g.bytemsg = "Too few bytes uploaded to earn more data -- please upload.";
961
962 if (!(g.leechmsg = get_param_ptr (TOPLEVEL_CONF, "LeechRatioMsg", FALSE)))
963 g.leechmsg = "10,000,000:1 CR: LEECH";
964
965 return 0;
966 }
967
968 /* Module API tables
969 */
970
971 static cmdtable ratio_cmdtab[] = {
972 { PRE_CMD, C_CWD, G_NONE, pre_cmd, FALSE, FALSE },
973 { CMD, C_CWD, G_NONE, cmd_cwd, FALSE, FALSE },
974
975 { PRE_CMD, C_LIST, G_NONE, pre_cmd, FALSE, FALSE },
976 { POST_CMD, C_LIST, G_NONE, ratio_post_cmd, FALSE, FALSE },
977
978 { PRE_CMD, C_NLST, G_NONE, pre_cmd, FALSE, FALSE },
979 { POST_CMD, C_NLST, G_NONE, ratio_post_cmd, FALSE, FALSE },
980
981 { PRE_CMD, C_RETR, G_NONE, pre_cmd_retr, FALSE, FALSE },
982 { POST_CMD, C_RETR, G_NONE, ratio_post_retr,FALSE, FALSE },
983
984 { PRE_CMD, C_STOR, G_NONE, pre_cmd, FALSE, FALSE },
985 { POST_CMD, C_STOR, G_NONE, ratio_post_stor,FALSE, FALSE },
986
987 { CMD, C_SITE, G_NONE, cmd_site, FALSE, FALSE },
988
989 { CMD, C_USER, G_NONE, cmd_user, FALSE, FALSE },
990
991 { POST_CMD, C_PASS, G_NONE, ratio_post_cmd, FALSE, FALSE },
992 { LOG_CMD, C_PASS, G_NONE, ratio_log_pass, FALSE, FALSE },
993
994 { 0, NULL }
995 };
996
997 static conftable ratio_conftab[] = {
998 { "UserRatio", add_ratiodata, NULL },
999 { "GroupRatio", add_ratiodata, NULL },
1000 { "AnonRatio", add_ratiodata, NULL },
1001 { "HostRatio", add_ratiodata, NULL },
1002 { "Ratios", set_ratios, NULL },
1003
1004 { "FileRatioErrMsg", add_str, NULL },
1005 { "ByteRatioErrMsg", add_str, NULL },
1006 { "LeechRatioMsg", add_str, NULL },
1007 { "CwdRatioMsg", add_str, NULL },
1008 { "SaveRatios", add_saveratios, NULL },
1009 { "RatioFile", add_str, NULL },
1010 { "RatioTempFile", add_str, NULL },
1011
1012 { NULL, NULL, NULL }
1013 };
1014
1015 module ratio_module = {
1016
1017 /* Always NULL */
1018 NULL, NULL,
1019
1020 /* Module API version */
1021 0x20,
1022
1023 /* Module name */
1024 "ratio",
1025
1026 /* Module configuration handler table */
1027 ratio_conftab,
1028
1029 /* Module command handler table */
1030 ratio_cmdtab,
1031
1032 /* Module authentication handler table */
1033 NULL,
1034
1035 /* Module initialization */
1036 NULL,
1037
1038 /* Session initialization */
1039 ratio_sess_init,
1040
1041 /* Module version */
1042 MOD_RATIO_VERSION
1043 };
1044