/*
* GProstats - GProFTPD Statistics generator.
* Copyright 2003 - 2006 Magnus Loef (Magnus-swe) <magnus-swe@telia.com>
*
* 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:
* <Anonymous /dir/path>
* User UserName
* ....
* </Anonymous>
*
*/
#include "../config.h"
#include <stdio.h>
#include <stdlib.h>
#include "chars_are_digits.h"
/* Solaris issue */
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#include <inttypes.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#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; x<argc; x++)
{
if( strstr(argv[x], "-help") )
{
show_usage(conf, xferlog);
exit(1);
}
if( argc>x+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, "<Anonymous /") )
max_rows++;
if( strstr(old_buffer, "<VirtualHost") )
found_virtualhost = 1;
/* Get the servername for the default server */
if( ! found_virtualhost )
{
if( strstr(old_buffer, "ServerName") )
{
snprintf(server_name, *old_buffer, old_buffer+11);
server_name[strlen(server_name)-1]='\0';
}
if( strstr(old_buffer, "ServerIdent on") )
{
snprintf(server_name, *old_buffer, old_buffer+15);
server_name[strlen(server_name)-1]='\0';
}
}
}
/* Allocate the elements of struct user_stats */
userlist = malloc(sizeof(struct user_stats)*max_rows);
user = allocate(8192);
dir = allocate(8192);
rewind(fp);
if( conf_size > 1 )
while(fgets(old_buffer, conf_size, fp)!=NULL)
{
if( strlen(old_buffer) > 8000 )
continue;
if( strstr(old_buffer, "<Anonymous /") )
{
/* Homedirectories */
snprintf(dir, 8000, "%s", old_buffer+11);
for(x=0; dir[x]; x++)
{
if(dir[x]=='>')
{
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, "</Anonymous>") )
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, "</Anonymous>") )
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, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n");
strcpy(html_welcome, "<html><head>\n");
strcat(html_welcome, "<meta http-equiv=\"CONTENT-TYPE\" content=\"text/html; charset=iso-8859-15\">\n");
strcat(html_welcome, "<title>GProFTPD statistics</title>\n");
strcat(html_welcome, "<meta name=\"GENERATOR\" content=\"OpenOffice.org 641 (Linux)\">\n");
strcat(html_welcome, "<meta name=\"CREATED\" content=\"20030324;20033300\">\n");
strcat(html_welcome, "<meta name=\"CHANGED\" content=\"20030325;11325700\">\n");
strcat(html_welcome, "<meta name=\"KEYWORDS\" content=\"GProFTPD statistics\">\n");
strcat(html_welcome, "<meta name=\"charset\" content=\"ISO-8859-1\">\n");
strcat(html_welcome, "<style>\n");
strcat(html_welcome, "th{ font-size: 24px; }\n");
strcat(html_welcome, "td{ font-size: 16px; }\n");
strcat(html_welcome, "td.left{ vertical-align: left; padding-left: 15%; }\n");
strcat(html_welcome, "td.right{ vertical-align: right; padding-right: 15%; }\n");
strcat(html_welcome, "</style></head>\n");
strcat(html_welcome, "<body lang=\"en-US\" text=\"#DDDDFF\" link=\"#4b6499\" vlink=\"#3b4581\" bgcolor=\"#3c278e\">\n");
strcat(html_welcome, "<table border=5 width=800 align=center>\n");
strcat(html_welcome, "<tr>\n");
strcat(html_welcome, "<td width=800 colspan=1><font size=5><center>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</center></td>\n");
strcat(html_welcome, "</tr></table>\n");
strcat(html_welcome, "<br>\n");
strcat(html_welcome, "<table border=2 width=600 align=center>\n");
strcat(html_welcome, "<tr>\n");
strcat(html_welcome, "<th width=600 colspan=2>Top 10 uploaders</th>\n");
strcat(html_welcome, "</tr>\n");
/* The top 10 dl section */
strcat(html_welcome1, "<table border=2 width=600 align=center>\n");
strcat(html_welcome1, "<tr>\n");
strcat(html_welcome1, "<th width=600 colspan=2>Top 10 downloaders</th>\n");
strcat(html_welcome1, "</tr>\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, "<tr><td class=left width=300>");
strcat(html_welcome, userlist[top_ul].user);
strcat(html_welcome, "</td>\n");
strcat(html_welcome, "<td align=right class=right width=300>");
sprintf(convert, "%llu", userlist[top_ul].ul);
strcat(html_welcome, convert);
strcat(html_welcome, " KB\n");
strcat(html_welcome, "</td></tr>\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, "<tr><td class=left width=300>");
strcat(html_welcome1, userlist[top_dl].user);
strcat(html_welcome1, "</td>\n");
strcat(html_welcome1, "<td class=right align=right width=300>");
sprintf(convert, "%llu", userlist[top_dl].dl);
strcat(html_welcome1, convert);
strcat(html_welcome1, " KB\n");
strcat(html_welcome1, "</td></tr>\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, "</table><br>\n");
strcat(html_welcome, html_welcome1);
strcat(html_welcome, "</table><br><br>\n");
if( strlen(datestamp) > 3 && strlen(datestamp) < 100 )
{
strcat(html_welcome, "<center>Statistics generated by GProftpd on \n");
strcat(html_welcome, datestamp);
strcat(html_welcome, "</center>\n");
}
strcat(html_welcome, "</body></html>\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;
}
syntax highlighted by Code2HTML, v. 0.9.1