/* * GProstats - GProFTPD Statistics generator. * Copyright 2003 - 2006 Magnus Loef (Magnus-swe) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * This is how it works: * ------------------------------ * Takes all users in proftpd.conf and if one if those users exists * in the xferlog those ul/dl stats gets added to that user. * The statistics are sorted by top 10 ul and dl. * It can generate html output and welcome messages for all users * that are currently listed like this in proftpd.conf: * * User UserName * .... * * */ #include "../config.h" #include #include #include "chars_are_digits.h" /* Solaris issue */ #ifdef HAVE_STDINT_H #include #else #include #endif #include #include #include #include "allocate.h" struct user_stats { char user[8192]; char dir[8192]; int ul_flag; int dl_flag; int update; uint64_t ul; uint64_t dl; }; void show_usage(char conf[8192], char xferlog[8192]) { printf("\nGProStats Usage:\n\n"); printf("-help Show this help.\n"); printf("-c %s\n", conf); printf("-x %s\n", xferlog); printf("-w Make welcome messages for all ftpusers with this name.\n"); printf("-html Make a html ftp statistics page in the specified location.\n\tIE: [/var/www/html/ftp_stats.html]\n\n"); printf("The default paths are [/etc/proftpd.conf] and [/var/log/xferlog]\n"); printf("The default welcome for gproftpd is [welcome.msg]\n\n"); printf("If neither of the -w or -html flags are used it will just print the results to the screen.\n"); printf("Only uploads and downloads above 1023 Bytes are counted.\n\n"); } int main(int argc, char *argv[]) { FILE *fp; struct user_stats * userlist; long conf_size; uint64_t file_size; char *old_buffer, *user, *dir, *action, *file, *datestamp, *welcome, *welcome1, *temp, *convert; /* Filepaths (limited to max 8000) */ char conf[8192]="", xferlog[8192]="", html[8192]="", welcome_name[8192]=""; /* Path to the users directory (+20 for welcome.msg and '/') (8192/8192+20) */ char user_welcome[16384+20]=""; /* The name of the server */ char server_name[8192]=""; /* HTML Part 1 (the lengths are max input from the entries) */ /* Can contain: static HTML(16384), username(128)*20 (2560), ul/dl KB(50)*20 (1000), * server name (1000), datestamp (100) and html_welcome1 */ char html_welcome[30000]=""; /* HTML Part 2 (the lengths are max input from the entries) * Can contain: static HTML(3000), username(128)*10 (1280), dl KB(50)*10 (500) */ char html_welcome1[5000]=""; int row=0, old_row=0, max_rows=0, count=0, x=0, z=0, top_ul=0, top_dl=0, timez=0, timex=0, once=0; int found_virtualhost = 0; /* Default settings, specified by configure and installed by a superuser */ strcpy(conf, PROFTPD_CONF); strcpy(xferlog, XFER_LOG); /* Check length of arguments */ for(x=0; argv[x]!=NULL; x++) { if( strlen(argv[x]) > 8000 ) { printf("\nGProStats error: supplied argument is too long\n"); show_usage(conf, xferlog); exit(1); } if( x > 1000 ) { printf("\nGProStats error: Max argument reached at 1000\n"); show_usage(conf, xferlog); exit(1); } if( strstr(argv[x], "%") ) { printf("\nGProStats error: supplied percent char in argument is invalid.\n"); show_usage(conf, xferlog); exit(1); } if( strstr(argv[x], "0x") ) { printf("\nGProStats error: supplied 0x chars in argument in invalid.\n"); show_usage(conf, xferlog); exit(1); } } printf("\n"); /* Check for user-supplied args */ for(x=0; xx+1 ) if( strstr(argv[x], "-c") && strstr(argv[x+1], "/") ) { printf("Using conf: %s\n", argv[x+1]); strcpy(conf, argv[x+1]); } if( argc>x+1 ) if( strstr(argv[x], "-x") && strstr(argv[x+1], "/") ) { printf("Using xferlog: %s\n", argv[x+1]); strcpy(xferlog, argv[x+1]); } if( argc>x+1 ) if( strstr(argv[x], "-w") && strlen(argv[x+1])>0 ) { printf("Creating welcome messages as: %s\n", argv[x+1]); strcpy(welcome_name, argv[x+1]); } if( argc>x+1 ) if( strstr(argv[x], "-html") && strstr(argv[x+1], "/") ) { printf("Making a html statistics page here: %s\n", argv[x+1]); strcpy(html, argv[x+1]); } } printf("\n"); /* Get usernames and homedirectories */ if((fp = fopen(conf, "r"))==NULL) { printf("\nproftpd.conf could not be found here: %s\n", conf); show_usage(conf, xferlog); return 0; } fseek(fp, 0, SEEK_END); conf_size = ftell(fp); rewind(fp); old_buffer = allocate(conf_size); if( conf_size > 1 ) while(fgets(old_buffer, conf_size, fp)!=NULL) { if( strlen(old_buffer) > 8000 ) continue; if( strstr(old_buffer, " 1 ) while(fgets(old_buffer, conf_size, fp)!=NULL) { if( strlen(old_buffer) > 8000 ) continue; if( strstr(old_buffer, "') { dir[x]='\0'; break; } } snprintf(userlist[row].dir, 8000, "%s", dir); while(fgets(old_buffer, conf_size, fp)!=NULL) { if( strlen(old_buffer) > 8000 ) continue; if( strstr(old_buffer, "") ) break; /* Username */ if( strstr(old_buffer, "User ") && ! strstr(old_buffer, "DirFakeUser") && ! strstr(old_buffer, "AllowUser") && ! strstr(old_buffer, "DenyUser") ) { sscanf(old_buffer, "%*s %s", user); snprintf(userlist[row].user, 8000, "%s", user); /* Init all to be updated */ userlist[row].update = 1; /* Locked .. dont update the welcome message */ while(fgets(old_buffer, conf_size, fp)!=NULL) { if( strlen(old_buffer) > 8000 ) continue; if( strstr(old_buffer, "#gplockstats") ) { userlist[row].update = 0; row--; } if( strstr(old_buffer, "") ) break; } row++; break; } } } } fclose(fp); free(old_buffer); /* Add users ul/dl statistics to the array */ action = allocate(8192); file = allocate(8192); datestamp = allocate(8192); temp = allocate(8192); if((fp = fopen(xferlog, "r")) == NULL) { printf("\nThe xferlog could not be found here: %s\n", xferlog); show_usage(conf, xferlog); return 0; } fseek(fp, 0, SEEK_END); conf_size = ftell(fp); rewind(fp); old_buffer = allocate(conf_size); if( conf_size > 1 ) while(fgets(old_buffer, conf_size, fp)!=NULL ) { if( strlen(old_buffer) > 8000 ) continue; /* DateStamp, 1-5 in the xferlog on the first line that has content */ if( ! once && strlen(old_buffer) > 5 ) { for(x=0; old_buffer[x]!='\n'; x++) { if( x > 100 ) { printf("Date is not in the correct place in the xferlog: %s\n", &old_buffer[0]); break; } if(old_buffer[x]==' ' && old_buffer[x+1]!=' ') z++; if( z == 5 ) { /* Security bug fixed thanks to Tavis Ormandy */ snprintf(datestamp, x+1, "%s", old_buffer); once = 1; break; } } } strcpy(user, ""); file_size = 0; /* User */ sscanf(old_buffer, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %s", user); /* Size */ strcpy(temp, ""); sscanf(old_buffer, "%*s %*s %*s %*s %*s %*s %*s %s", temp); if( chars_are_digits(temp) ) sscanf(old_buffer, "%*s %*s %*s %*s %*s %*s %*s %llu", &file_size); /* UL or DL */ sscanf(old_buffer, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %s", action); /* File */ sscanf(old_buffer, "%*s %*s %*s %*s %*s %*s %*s %*s %s", file); /* Go thru the array looking for this user and add its ul/dl stats */ row = 0; while( row < max_rows ) { if( ! strcmp(userlist[row].user, user) ) { /* Upload */ if( strstr(action, "i") ) userlist[row].ul=userlist[row].ul+file_size/1024; /* Download */ if( strstr(action, "o") ) userlist[row].dl=userlist[row].dl+file_size/1024; } row++; } } fclose(fp); free(old_buffer); free(temp); free(user); free(dir); free(action); free(file); welcome = allocate(16384); welcome1 = allocate(16384); strcpy(welcome, "Welcome to "); strcat(welcome, server_name); strcat(welcome, " \%U\n\n"); strcat(welcome, "You are user \%N out of a maximum of \%M authorized logins.\n"); strcat(welcome, "Current time is \%T.\n"); strcat(welcome, "The administrator can be reached here: \%E\n"); if( strlen(datestamp) > 3 && strlen(datestamp) < 100 ) { strcat(welcome, "\nStatistics since: "); strcat(welcome, datestamp); } strcat(welcome, "\n\nTop 10 Uploaders:\n_________________\n"); /* Generate html statistics */ if( strlen(html) > 1 ) { /* The page and the top 10 ul */ strcpy(html_welcome, "\n"); strcpy(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "GProFTPD statistics\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "
Statistics for "); strcat(html_welcome, server_name); if( strlen(datestamp) > 3 && strlen(datestamp) < 100 ) { strcat(html_welcome, " since: "); strcat(html_welcome, datestamp); } strcat(html_welcome, "\n
\n"); strcat(html_welcome, "
\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); /* The top 10 dl section */ strcat(html_welcome1, "
Top 10 uploaders
\n"); strcat(html_welcome1, "\n"); strcat(html_welcome1, "\n"); strcat(html_welcome1, "\n"); } free(datestamp); /* Calculate top 10 UL and top 10 DL */ x=0; z=0; row=0; count=0; old_row=0; convert = allocate(8192); while( 1 ) { if( row > max_rows-1 ) row = 0; if( count >= max_rows * max_rows ) break; old_row = 0; timez = 0; timex = 0; while( 1 ) { /* Greater or equal UL then any user in the list thats not flagged */ if( userlist[row].ul >= userlist[old_row].ul && ! userlist[row].ul_flag && ! userlist[old_row].ul_flag ) { top_ul = row; timez++; } /* Greater or equal DL then any user in the list thats not flagged */ if( userlist[row].dl >= userlist[old_row].dl && ! userlist[row].dl_flag && ! userlist[old_row].dl_flag ) { top_dl = row; timex++; } /* .. Or if the user is flagged */ if( userlist[old_row].ul_flag ) timez++; if( userlist[old_row].dl_flag ) timex++; /* TOP UPLOAD */ if( timez > max_rows-1 && userlist[top_ul].ul !=0 && userlist[top_ul].ul_flag !=1 ) { z++; if( z <= 10 ) { userlist[top_ul].ul_flag = 1; strcat(welcome, userlist[top_ul].user); strcat(welcome, ": "); sprintf(convert, "%llu", userlist[top_ul].ul); strcat(welcome, convert); strcat(welcome, " KB\n"); /* Generate html */ if( strlen(html) > 1 ) { strcat(html_welcome, "\n"); strcat(html_welcome, "\n"); } } } /* TOP Download */ if( timex > max_rows-1 && userlist[top_dl].dl !=0 && userlist[top_dl].dl_flag !=1 ) { x++; if( x<=10 ) { userlist[top_dl].dl_flag = 1; strcat(welcome1, userlist[top_dl].user); strcat(welcome1, ": "); sprintf(convert, "%llu", userlist[top_dl].dl); strcat(welcome1, convert); strcat(welcome1, " KB\n"); /* Generate html */ if( strlen(html) > 1 ) { strcat(html_welcome1, "\n"); strcat(html_welcome1, "\n"); } } } if( old_row < max_rows-1 ) old_row++; else break; } count++; row++; } free(convert); strcat(welcome, "\nTop 10 Downloaders:\n"); strcat(welcome, "___________________\n"); strcat(welcome, welcome1); strcat(welcome, "\n"); /* Get todays date */ datestamp = allocate(8192); if((fp=popen("date", "r"))==NULL) { printf("Pipe Fork error [get date 2]\n"); return 0; } fflush(fp); while(fgets(datestamp, 8192, fp)!=NULL) { if( strlen(datestamp) > 3 && strlen(datestamp) < 100 ) { strcat(welcome, "\nGenerated on "); strcat(welcome, datestamp); strcat(welcome, "\n"); break; } } pclose(fp); printf("\n%s\n", welcome); /* Add welcome messages for all ftp users in proftpd.conf that should be updated */ if( strlen(welcome_name) > 1 ) { row=0; while(1) { if( strlen(userlist[row].dir) > 0 ) { /* Minimize writing to the same file */ if( row > 0 && row < max_rows-1 ) { if( ! strcmp(userlist[row].dir, userlist[row-1].dir) ) { row++; continue; } } /* Dont write a welcome.msg if theres a "gplock" lockfile present */ if( userlist[row].update ) { /* Write the welcome.msg */ strcpy(user_welcome, userlist[row].dir); if( user_welcome[strlen(user_welcome)-1]!='/' ) strcat(user_welcome, "/"); strcat(user_welcome, welcome_name); if((fp=fopen(user_welcome, "w+"))==NULL) { printf("\nSomething is wrong with proftpd.conf, could not write to this dir:\n%s\n", userlist[row].dir); } else { fputs(welcome, fp); fclose(fp); } } } if( row < max_rows-1 ) row++; else break; } } /* Output generated html */ if( strlen(html) > 1 ) { strcat(html_welcome, "
Top 10 downloaders
"); strcat(html_welcome, userlist[top_ul].user); strcat(html_welcome, ""); sprintf(convert, "%llu", userlist[top_ul].ul); strcat(html_welcome, convert); strcat(html_welcome, " KB\n"); strcat(html_welcome, "
"); strcat(html_welcome1, userlist[top_dl].user); strcat(html_welcome1, ""); sprintf(convert, "%llu", userlist[top_dl].dl); strcat(html_welcome1, convert); strcat(html_welcome1, " KB\n"); strcat(html_welcome1, "

\n"); strcat(html_welcome, html_welcome1); strcat(html_welcome, "

\n"); if( strlen(datestamp) > 3 && strlen(datestamp) < 100 ) { strcat(html_welcome, "
Statistics generated by GProftpd on \n"); strcat(html_welcome, datestamp); strcat(html_welcome, "
\n"); } strcat(html_welcome, "\n"); if((fp = fopen(html, "w+")) == NULL) { printf("\nCould not write: %s\n", html); show_usage(conf, xferlog); return 0; } else { printf("Adding html statistics to: %s\n", html); fputs(html_welcome, fp); fclose(fp); } } free(welcome); free(welcome1); free(datestamp); /* Free the struct */ free(userlist); if( argc < 2 ) { printf("\nThe output shown was never written as welcome messages.\n"); printf("Add \"-w welcome.msg\" and they will be written, use --help for help.\n\n"); } return 0; }