Subject: quota logon processing for ftp users?

From: Albert Lunde <[email-masked]>
Date: Mon, 18 Nov 1996 18:44:34 -0600

I'm setting up a web server where normal users will have ftp-only access to
post their web pages. One of the questions which arises is how do users
know when they are over the disk quota.

I could do some things with the normal wuftp server like put quota or du
(or scripts containing them) where they could be run with the SITE EXEC
command. But the ways to access this with a GUI ftp client are pretty
tricky.

I'm thinking it could be useful to patch the source to run something like
quota -v for real users and pass that back as part of the post-logon
messages.

Has anyone done something like this? Do you have any advice?

(I'm guessing that looking for the processing of "readme" and "message"
directives will put me at about the right part of the code, but I haven't
started to look at the sources.)

(I'm also thinking about some unrelated solutions like a web cgi and/or a
cron job to nag users.)

---
    Albert Lunde                      [email-masked]

---

From: [email-masked] (Koos van den Hout _U nix and we all_)
Date: Tue, 19 Nov 1996 18:22:39 +0100 (MET)

> Has anyone done something like this? Do you have any advice?

Yes, someone has. Below my .sig is a repost from a patch doing this for
AIX and maybe some other systems. You might have to adopt it for your
own system, but it's a start. It even saves calling quota (although
that might be more portable).

If someone could test the portability of this, maybe it could be
integrated into the normal source tree. Quota's are a well-used part of
system management these days so wu-ftpd could do something usefull with
them.

                                       Grtx KH

-- 
  Koos van den Hout,     Internetter, Unix freak, ISFJ and BBS SysOp at large
  [email-masked] (Home)        BBS Koos z'n Doos (+31-30-6036637 28k8)
  [email-masked] (Work)  <-- finger -l for PGPkey    (+31-30-6056619 14k4)
  http://www.hvu.nl/~koos (WWW)    Looking for a license plate with "RFC 822"

>From pizza.hvu.nl  Thu May 23 22:56:54 1996
>Return-Path: <[email-masked]>
>Received: from wugate.wustl.edu by pizza.hvu.nl (SMI-8.6/KH19960412)
>	id WAA05155; Thu, 23 May 1996 22:56:52 +0200
>Received: from host by wugate.wustl.edu (8.6.12/8.6.11) with SMTP id PAA18511; Thu, 23 May 1996 15:49:22 -0500
>Received: from aristo.tau.ac.il by wugate.wustl.edu (8.6.12/8.6.11) with ESMTP id PAA18088 for <[email-masked]>; Thu, 23 May 1996 15:43:54 -0500
>Received: (from eilon@localhost) by aristo.tau.ac.il (8.7.5/8.7.5) id XAA07832; Thu, 23 May 1996 23:43:26 +0300 (IDT)
>Message-Id: <[email-masked]>
>Date: Thu, 23 May 1996 23:43:25 +0300 (IDT)
>Reply-To: [email-masked]
>Sender: [email-masked]
>From: Eilon Gishri <[email-masked]>
>To: [email-masked] (wu-ftpd), [email-masked]
>Subject: Quota magic cookies
>MIME-Version: 1.0
>Content-Type: text/plain; charset=US-ASCII
>Content-Transfer-Encoding: 7bit
>X-Mailer: ELM [version 2.4ME+ PL17 (25)]
>X-Listprocessor-Version: 7.1 -- ListProcessor by CREN
>content-length: 6573
>Status: RO

Here is the code with the mapping of the magic cookies.

Forgive me for being lazy...

struct	dqblk {
%B	u_long	dqb_bhardlimit;	/* absolute limit on disk blks alloc */
%b	u_long	dqb_bsoftlimit;	/* preferred limit on disk blks */
%Q	u_long	dqb_curblocks;	/* current block count */
%I	u_long	dqb_ihardlimit;	/* maximum # allocated inodes + 1 */
%i	u_long	dqb_isoftlimit;	/* preferred inode limit */
%q	u_long	dqb_curinodes;	/* current # allocated inodes */
%H	time_t	dqb_btime;	/* time limit for excessive disk use */
%h	time_t	dqb_itime;	/* time limit for excessive files */
};

diff -u -r --new-file wu-ftpd-2.4.2-beta-10.old/src/config/config.aix wu-ftpd-2.4.2-beta-10/src/config/config.aix
--- wu-ftpd-2.4.2-beta-10.old/src/config/config.aix	Tue Dec  5 06:31:28 1995
+++ wu-ftpd-2.4.2-beta-10/src/config/config.aix	Thu May 23 17:48:46 1996
@@ -34,3 +34,7 @@
 typedef void	SIGNAL_TYPE;
 
 #include "../config.h"
+
+#define VIRTUAL
+#define QUOTA
+#define CANT_BIND
diff -u -r --new-file wu-ftpd-2.4.2-beta-10.old/src/extensions.c wu-ftpd-2.4.2-beta-10/src/extensions.c
--- wu-ftpd-2.4.2-beta-10.old/src/extensions.c	Sat Mar 16 07:00:08 1996
+++ wu-ftpd-2.4.2-beta-10/src/extensions.c	Thu May 23 18:31:42 1996
@@ -63,6 +63,16 @@
 #include "extensions.h"
 #include "support/ftw.h"
 
+#ifdef QUOTA
+struct dqblk quota;
+char
+#ifdef __STDC__
+*time_quota(long curstate, long softlimit, long timelimit);
+#else
+*time_quota();
+#endif
+#endif /* QUOTA */
+
 #ifdef HAVE_REGEX_H
 #include <regex.h>
 #endif
@@ -184,6 +194,9 @@
 {
     char *inptr = inbuf;
     char *outptr = outbuf;
+#ifdef QUOTA
+    char *timeleft;
+#endif
     char buffer[MAXPATHLEN];
     time_t curtime;
     int limit;
@@ -264,6 +277,44 @@
                 *(outptr + 24) = '\0';
                 break;
 
+#ifdef QUOTA		/* eilon : cookies for quota */
+            case 'B':
+                sprintf(outptr, "%d", quota.dqb_bhardlimit);
+                break;
+
+            case 'b':
+                sprintf(outptr, "%d", quota.dqb_bsoftlimit);
+                break;
+
+            case 'Q':
+                sprintf(outptr, "%d", quota.dqb_curblocks);
+                break;
+
+            case 'I':
+                sprintf(outptr, "%d", quota.dqb_ihardlimit);
+                break;
+
+            case 'i':
+                sprintf(outptr, "%d", quota.dqb_isoftlimit);
+                break;
+
+            case 'q':
+                sprintf(outptr, "%d", quota.dqb_curinodes);
+                break;
+
+            case 'H':
+                timeleft = time_quota(quota.dqb_curblocks,quota.dqb_bsoftlimit,
+				quota.dqb_btime);
+                strcpy(outptr, timeleft);
+                break;
+
+            case 'h':
+                timeleft = time_quota(quota.dqb_curinodes,quota.dqb_isoftlimit,
+				quota.dqb_itime);
+                strcpy(outptr, timeleft);
+                break;
+#endif /* QUOTA */
+
             case '%':
                 *outptr++ = '%';
                 *outptr = '\0';
@@ -1152,7 +1203,80 @@
    return 0;
 }
 
+#ifdef QUOTA
+void
+#ifdef __STDC__
+get_quota(char *fs,int uid)
+#else
+get_quota(fs, uid)
+char *fs;
+int uid;
+#endif
+{
+#ifdef AIX
+  if(quotactl(fs,QCMD(Q_GETQUOTA,USRQUOTA),uid,&quota))
+#else
+  if(quotactl(Q_GETQUOTA,fs,uid,&quota))
+#endif
+     syslog(LOG_WARNING, "quotactl (Q_GETQUOTA): %m");
+}
 
+char
+#ifdef __STDC__
+*time_quota(long curstate, long softlimit, long timelimit)
+#else
+*time_quota(curstate, softlimit, timelimit)
+long curstate, softlimit, timelimit;
+#endif
+{
+	struct timeval tv;
+	char timeleft[80];
 
+	gettimeofday(&tv, NULL);
+	if (softlimit && curstate >= softlimit) {
+		if (timelimit == 0) {
+			strcpy(timeleft, "NOT STARTED");
+		} else if (timelimit > tv.tv_sec) {
+			fmttime(timeleft, timelimit - tv.tv_sec);
+		} else {
+			strcpy(timeleft, "EXPIRED");
+		}
+	} else {
+		timeleft[0] = '\0';
+	}
+	return(timeleft);
+}
 
-
+void
+#ifdef __STDC__
+fmttime(char *buf, register long time)
+#else
+fmttime(buf, time)
+	char *buf;
+	register long time;
+#endif
+{
+	int i;
+	static struct {
+		int c_secs;		/* conversion units in secs */
+		char * c_str;		/* unit string */
+	} cunits [] = {
+		{60*60*24*28, "months"},
+		{60*60*24*7, "weeks"},
+		{60*60*24, "days"},
+		{60*60, "hours"},
+		{60, "mins"},
+		{1, "secs"}
+	};
+
+	if (time <= 0) {
+		strcpy(buf, "EXPIRED");
+		return;
+	}
+	for (i = 0; i < sizeof(cunits)/sizeof(cunits[0]); i++) {
+		if (time >= cunits[i].c_secs)
+			break;
+	}
+	sprintf(buf, "%.1f %s", (double)time/cunits[i].c_secs, cunits[i].c_str);
+}
+#endif /* QUOTA */
diff -u -r --new-file wu-ftpd-2.4.2-beta-10.old/src/extensions.h wu-ftpd-2.4.2-beta-10/src/extensions.h
--- wu-ftpd-2.4.2-beta-10.old/src/extensions.h	Fri Apr  1 22:03:40 1994
+++ wu-ftpd-2.4.2-beta-10/src/extensions.h	Thu May 23 17:48:45 1996
@@ -44,3 +44,16 @@
 #define ARG8    entry->arg[8]
 #define ARG9    entry->arg[9]
 #define ARG     entry->arg
+
+#ifdef QUOTA
+#include<sys/time.h>
+#ifdef IRIX
+#include<sys/quota.h>
+#endif
+#ifdef SUNOS
+#include<ufs/quota.h>
+#endif
+#ifdef AIX
+#include<jfs/quota.h>
+#endif
+#endif /* QUOTA */
diff -u -r --new-file wu-ftpd-2.4.2-beta-10.old/src/ftpd.c wu-ftpd-2.4.2-beta-10/src/ftpd.c
--- wu-ftpd-2.4.2-beta-10.old/src/ftpd.c	Sat Mar 16 07:00:09 1996
+++ wu-ftpd-2.4.2-beta-10/src/ftpd.c	Thu May 23 17:48:45 1996
@@ -211,6 +211,10 @@
 char virtual_logfile[MAXPATHLEN];
 #endif
 
+#ifdef QUOTA
+extern struct dqblk quota;
+#endif
+
 int data;
 jmp_buf errcatch,
   urgcatch;
@@ -1418,10 +1422,13 @@
         /* We MUST do a chdir() after the chroot. Otherwise the old current
          * directory will be accessible as "." outside the new root! */
 #ifdef VIRTUAL
-      if (virtual_mode) {
+      if (virtual_mode && !guest) {
         strcpy(pw->pw_dir, virtual_root);
       }
 #endif 
+#ifdef QUOTA
+	get_quota(pw->pw_dir,pw->pw_uid);
+#endif
         if (anonymous) {
             if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
                 reply(550, "Can't set guest privileges.");
@@ -2072,6 +2079,11 @@
     /* anchor socket to avoid multi-homing problems */
     data_source.sin_family = AF_INET;
     data_source.sin_addr = ctrl_addr.sin_addr;
+
+#if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */
+    data_source.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif 
+
     for (tries = 1;; tries++) {
         if (bind(s, (struct sockaddr *) &data_source,
                  sizeof(data_source)) >= 0)

-- 
		Eilon Gishri, Tel-Aviv University Computation Center
		Home 03-5078671
		E-mail: [email-masked]

---

From: Eilon Gishri <[email-masked]>
Date: Thu, 21 Nov 1996 19:26:14 +0200 (IST)

> system management these days so wu-ftpd could do something usefull with
  I was the one who hacked it. I'm using it on AIX since then. The main problem
here, in my opinion (which is quite small if you look at it) it to put the right
include file AKA - #include<SYS_FS/quota.h>. You fixed it, you're on the right
track. If you need any help porting it to other systems I'd be happy to help.

  I'm now thinking of hacking something of a "directory quota", this should answer
the problem of the "upload" keyword with other username. Meaning you should be
able to get with the right magic cookie the same info you get now with my patch
but for a different username. a couple of MUSTS (probably, for security reasons):

1) There should be an upload keyword for it (We do not need someone playing
with the cookies and asking for other user's quota).

2) Permissions and file owners of ftpaccess and the file which holds the cookies.

Comments please.

-- 
		Eilon Gishri, Tel-Aviv University Computation Center
		Home 03-5078671 /* a matter of national security */
		E-mail: [email-masked]