File: tokenfs.pike

package info (click to toggle)
libroxen-tokenfs 1.1-7
  • links: PTS
  • area: main
  • in suites: woody
  • size: 164 kB
  • sloc: makefile: 41
file content (469 lines) | stat: -rw-r--r-- 11,960 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
// This is a token "file-system".
// It will be located somewhere in the name-space of the server.

/*
Author: Allen Bolderoff <allen@gist.net.au>
License: GNU General Public License (see the COPYING FILE in this archive)
WARRANTY: NONE - forget it!!! :-)
*/


string cvs_version= "$Id: tokenfs.pike,v 1.1 1997/09/12 14:23:54 allen Exp $"; 

int thread_safe=1;


#include <module.h>
#include <roxen.h>
#include <stat.h>

#if DEBUG_LEVEL > 20
# ifndef FILESYSTEM_DEBUG
#  define FILESYSTEM_DEBUG
# endif
#endif

inherit "module";
inherit "roxenlib";
inherit "socket";

import Array;

object sql;
int redirects, accesses, errors, dirlists;
int puts, deletes;

static int do_stat = 1;

string status()
{
  return ("<h2>Accesses to this filesystem</h2>"+
	  (redirects?"<b>Redirects</b>: "+redirects+"<br>":"")+
	  (accesses?"<b>Normal files</b>: "+accesses+"<br>"
	   :"No file accesses<br>")+
	  (QUERY(put)&&puts?"<b>Puts</b>: "+puts+"<br>":"")+
	  (QUERY(delete)&&deletes?"<b>Deletes</b>: "+deletes+"<br>":"")+
	  (errors?"<b>Permission denied</b>: "+errors
	   +" (not counting .htaccess)<br>":"")+
	  (dirlists?"<b>Directories</b>:"+dirlists+"<br>":""));
}

void expire_users()
{
	int lts=(time());
	mapping(string:int) lt = localtime(lts);
	string s = sprintf("%04d%02d%02d%02d%02d%02d",
                   lt->year+1900, lt->mon+1, lt->mday-1,
                   lt->hour, lt->min, lt->sec);
	perror("s is equal to "+s+"\n");
	string getexpusers="select username FROM users WHERE filler<='"+s+"'";
	foreach(sql->query(getexpusers), mapping(string:mixed) un) {
		string accesses="DELETE FROM accesses WHERE username='"
				+un->username+"'";
		string userdetails="DELETE FROM userdetails WHERE username='"
				+un->username+"'";
		string users="DELETE FROM users WHERE username='"
				+un->username+"'";
		perror("about to remove user "+un->username+"\n");
		perror("his filler date is  "+un->filler+"\n");
		sql->query(accesses);
		sql->query(userdetails);
		sql->query(users);
	}
}
void create()
{
  defvar("mountpoint", "/", "Mount point", TYPE_LOCATION, 
	 "This is where the module will be inserted in the "+
	 "namespace of your server.");

  defvar("searchpath", "/var/www/tokenfs/", "Search path", TYPE_DIR,
	 "This is where the module will find the files in the real "+
	 "file system");

  defvar("tokenvalue", 1, "Token Cost", TYPE_INT,
	 "This is the cost of accessing directories in this tree.");

  defvar("name", "Bob's Shop", "Store Name", TYPE_STRING,
	 "The name of the store.");

  defvar("dbhost", "localhost", "Database: Host", TYPE_STRING,
	 "This is the hostname running the Auth Database.");

  defvar("dbname", "token", "Database: DB Name", TYPE_STRING,
	 "This is the name of the Auth Database.");

  defvar("dbuser", "token", "Database: DB User", TYPE_STRING,
	 "This is the name of the user of the Auth Database.");

  defvar("dbpassword", "", "Database: DB Password ", TYPE_STRING,
	 "This is the name of the Auth Database password.");

  defvar("notokens", "/notokens.html", "Errors: No Tokens", TYPE_STRING,
	 "This is an html file returned when user is out of tokens.");

  defvar("timelimit", 2, "Timelimit", TYPE_INT,
	 "This is the default timelimit for accounts\n"
	 "\nBasically, we want the account to expire (& be removed)"
	 "\nwithin X days\n.");



#ifdef COMPAT
  defvar("html", 0, "All files are really HTML files", TYPE_FLAG|VAR_EXPERT,
	 "If you set this variable, the filesystem will _know_ that all files "
	 "are really HTML files. This might be useful now and then.");
#endif

  defvar(".files", 0, "Show hidden files", TYPE_FLAG|VAR_MORE,
	 "If set, hidden files will be shown in dirlistings and you "
	 "will be able to retrieve them.");

  defvar("dir", 1, "Enable directory listings per default", TYPE_FLAG|VAR_MORE,
	 "If set, you have to create a file named .www_not_browsable ("
	 "or .nodiraccess) in a directory to disable directory listings."
	 " If unset, a file named .www_browsable in a directory will "
	 "_enable_ directory listings.\n");

  defvar("tilde", 0, "Show backupfiles", TYPE_FLAG|VAR_MORE,
	 "If set, files ending with '~' or '#' or '.bak' will "+
	 "be shown in directory listings");

  defvar("stat_cache", 1, "Cache the results of stat(2)",
	 TYPE_FLAG|VAR_MORE,
	 "This can speed up the retrieval of files up to 60/70% if you"
	 " use NFS, but it does use some memory.");

  defvar("no_symlinks", 0, "Forbid access to symlinks", TYPE_FLAG|VAR_MORE,
	 "EXPERIMENTAL.\n"
	 "Forbid access to paths containing symbolic links.<br>\n"
	 "NOTE: This can cause *alot* of lstat system-calls to be performed "
	 "and can make the server much slower.");
}


mixed *register_module()
{
  return ({ 
    MODULE_LOCATION, 
    "Token Filesystem", 
    "This is a token filesystem for e-commerce, use it to make files "
    "available to people who have purchased tokens."
  });
}

string path;
int stat_cache;

void start()
{
  path = QUERY(searchpath);
  stat_cache = QUERY(stat_cache);
#ifdef FILESYSTEM_DEBUG
  perror("FILESYSTEM: Online at "+QUERY(mountpoint)+" (path="+path+")\n");
#endif
}

string query_location()
{
  return QUERY(mountpoint);
}


mixed stat_file( mixed f, mixed id )
{
  object privs;

  if (((int)id->misc->uid) && ((int)id->misc->gid) &&
      (QUERY(access_as_user))) {
    // NB: Root-access is prevented.
    privs=((program)"privs")("Getting file", (int)id->misc->uid, 
			     (int)id->misc->gid );
  }
  if(!stat_cache)
    return file_stat(path + f); /* No security currently in this function */
  array fs;
  if(!id->pragma["no-cache"]&&(fs=cache_lookup("stat_cache",path+f)))
    return fs;
  fs = file_stat(path+f);
  cache_set("stat_cache",path+f,fs);
  return fs;
}

string real_file( mixed f, mixed id )
{
  if(this->stat_file( f, id )) 
/* This filesystem might be inherited by other filesystem, therefore
   'this'  */
    return path + f;
}

int dir_filter_function(string f)
{
  if(f[0]=='.' && !QUERY(.files))           return 0;
  if(!QUERY(tilde) && backup_extension(f))  return 0;
  return 1;
}

array find_dir( string f, object id )
{
  mixed ret;
  array dir;

  object privs;

  if (((int)id->misc->uid) && ((int)id->misc->gid) &&
      (QUERY(access_as_user))) {
    // NB: Root-access is prevented.
    privs=((program)"privs")("Getting file", (int)id->misc->uid, 
			     (int)id->misc->gid );
  }

  if(!(dir = get_dir( path + f )))
    return 0;

  privs = 0;

  if(!QUERY(dir))
    // Access to this dir is allowed.
    if(search(dir, ".www_browsable") == -1)
    {
      errors++;
      return 0;
    }

  // Access to this dir is not allowed.
  if(sizeof(dir & ({".nodiraccess",".www_not_browsable",".nodir_access"})))
  {
    errors++;
    return 0;
  }

  dirlists++;

  // Pass _all_ files, hide none.
  if(QUERY(tilde) && QUERY(.files)) /* This is quite a lot faster */
    return dir;

  return filter(dir, dir_filter_function);
}


int _file_size(string X,object id)
{
  array fs;
  if(!id->pragma["no-cache"]&&(fs=cache_lookup("stat_cache",(X))))
  {
    id->misc->stat = fs;
    return fs[ST_SIZE];
  }
  if(fs = file_stat(X))
  {
    id->misc->stat = fs;
    cache_set("stat_cache",(X),fs);
    return fs[ST_SIZE];
  }
  return -1;
}

#define FILE_SIZE(X) (stat_cache?_file_size((X),id):Stdio.file_size(X))

int contains_symlinks(string root, string path)
{
  array arr = path/"/";

  foreach(arr - ({ "" }), path) {
    root += "/" + path;
    if (arr = file_stat(root, 1)) {
      if (arr[1] == -3) {
	return(1);
      }
    } else {
      return(0);
    }
  }
  return(0);
}


string validate_user(object id)
{
  if(!id->realauth)
    return 0;
  string user = (id->realauth/":")[0];
  string key = (id->realauth/":")[1];

  string ndbhost = query("dbhost");
  string ndbname = query("dbname");
  string ndbuser = query("dbuser");
  string ndbpass = query("dbpassword");

  sql=Sql.sql(ndbhost, ndbname, ndbuser, ndbpass);
  array result=sql->query("SELECT username,password from users WHERE username='"+user+"'");

  if(sizeof(result)==1 && crypt(key, result[0]->password)==1)
	return result[0]->username;

  return 0;
}
           


mixed find_file( string f, object id )
{
  object o;
  int size;
  string tmp;
  string oldf = f;
  string user;
#ifdef FILESYSTEM_DEBUG
  perror("FILESYSTEM: Request for "+f+"\n");
#endif


if(!(user = validate_user(id)))
    return (["type":"text/html",
                   "error":401,
                   "extra_heads":
                     ([ "WWW-Authenticate":
                        "basic realm=\""+query("name")+"\""]),
                   "data":"<title>Access Denied</title>"
                   "<h2 align=center>Access forbidden</h2>"
            ]); 

  array(string) fn;
	fn=(f/"/"); 
  

//	At This point, we need to do a check to see if
//      the directory being accessed is the same as the mount point,
//	in which case, we just skip the charge (and or log the access),
//	else we charge it accordingly.

  
  if(FILE_SIZE(query("searchpath")+fn[0])==-2)	// We've got a directory... check tokens.
  {

	

//This next bit checks whether the user has been online before
//and sets the filler date for 2 days from now if he/she hasn't

array newuser=sql->query("SELECT * FROM accesses WHERE username='" 
			+user+"'");
mapping(string:int) lt = localtime(time());
int timelimit=query("timelimit");
string timeadj = sprintf("%04d%02d%02d%02d%02d%02d",
                   lt->year+1900, lt->mon+1, lt->mday+timelimit,
                   lt->hour, lt->min, lt->sec);

	if(sizeof(newuser)==0)
	{
		newuser=sql->query("UPDATE users SET filler='"
			+timeadj+"' WHERE "
			"username='" +user+ "';");
		perror("The Sql statement to update the time is\n"
			"UPDATE users SET filler='"
			+timeadj+
			"' WHERE username='" +user+ "'");
	}

expire_users();

	array r=sql->query("SELECT * FROM accesses WHERE directory='"
		+query("mountpoint")+fn[0]+
		"' and username='" +user+"'");
	if(sizeof(r)==0)	// New directory, deduct tokens.
	{	
		r=sql->query("SELECT tokens FROM users WHERE username='"
			+user+"' AND tokens >="+query("tokenvalue"));
		if(sizeof(r)==0)
			return http_redirect(query("notokens")+"?user="+user, id);
		else { sql->query("UPDATE users SET tokens=tokens-"+
			query("tokenvalue")+" WHERE username='"+
			user+"'");
			perror("Deducting Tokens for "+user+"\n");
			sql->query("INSERT INTO accesses VALUES('"+
				user+"','"+query("mountpoint")+fn[0]+
				"',NULL)");

			}
	}

  }

  size = FILE_SIZE( f = path + f );

  switch(id->method)
  {
  case "GET":
  case "HEAD":
  case "POST":
  
    switch(-size)
    {
    case 1:
      return 0; /* Is no-file */

    case 2:
      return -1; /* Is dir */

    default:
      if(f[ -1 ] == '/') /* Trying to access file with '/' appended */
      {
	/* Do not try redirect on top level directory */
	if(sizeof(id->not_query) < 2)
	  return 0;
	redirects++;
	return http_redirect(id->not_query[..sizeof(id->not_query)-2], id);
      }

      if(!id->misc->internal_get && QUERY(.files)
	 && (tmp = (id->not_query/"/")[-1])
	 && tmp[0] == '.')
	return 0;

      object privs;

      if (((int)id->misc->uid) && ((int)id->misc->gid) &&
	  (QUERY(access_as_user))) {
	// NB: Root-access is prevented.
	privs=((program)"privs")("Getting file", (int)id->misc->uid, 
			       (int)id->misc->gid );
      }

      o = open( f, "r" );

      privs = 0;

      if(!o || (QUERY(no_symlinks) && (contains_symlinks(path, oldf))))
      {
	errors++;
	report_error("Open of " + f + " failed. Permission denied.\n");
	return http_low_answer(403, "<h2>File exists, but access forbidden "
			       "by user</h2>");
      }

      id->realfile = f;
      accesses++;
#ifdef COMPAT
      if(QUERY(html)) /* Not very likely, really.. */
	return ([ "type":"text/html", "file":o, ]);
#endif
      return o;
    }
    break;
  
  default:
    return 0;
  }
  report_error("Not reached..\n");
  return 0;
}

string query_name()
{
  return sprintf("<i>%s</i> mounted on <i>%s</i>", query("searchpath"),
		 query("mountpoint"));
}