httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Dirk vanGulik <Dirk.vanGu...@jrc.it>
Subject loggin in anon
Date Wed, 24 Jul 1996 17:56:34 GMT
This is some logging for mod_auth_anon, forgot who
asked for it; this is not for inclusion in the core,
just to be informative, etc.

Dw.
*** /enrm/httpd/depot/apache_19960606010017/src/mod_auth_anon.c	Wed May 29 09:00:22 1996
--- /enrm/httpd/src/mod_auth_anon.c	Thu Jun 13 14:58:49 1996
***************
*** 59,65 ****
   * 
   * Adapted to Shambhala by rst.
   *
!  * Version 0.5 May 1996
   *
   * Brutally raped by Dirk.vanGulik@jrc.it to
   * 
--- 59,65 ----
   * 
   * Adapted to Shambhala by rst.
   *
!  * Version 0.7 Jun 1996
   *
   * Brutally raped by Dirk.vanGulik@jrc.it to
   * 
***************
*** 69,81 ****
   *
   * Just add the following tokes to your <directory> setup:
   * 
!  * Anonymous 			magic-user-id [magic-user-id]...
   *
!  * Anonymous_MustGiveEmail	[ on | off ] default = off
!  * Anonymous_LogEmail		[ on | off ] default = on
!  * Anonymous_VerifyEmail	[ on | off ] default = off
!  * Anonymous_NoUserId		[ on | off ] default = off
!  * Anonymous_Authorative        [ on | off ] default = off
   *
   * The magic user id is something like 'anonymous', it is NOT case sensitive. 
   * 
--- 69,85 ----
   *
   * Just add the following tokes to your <directory> setup:
   * 
!  * - Anonymous 			< magic-user-id [magic-user-id]...>
   *
!  * + Anonymous_MustGiveEmail	[ on | off ] default = off
!  * + Anonymous_LogEmail		[ on | off ] default = on
!  * + Anonymous_VerifyEmail	[ on | off ] default = off
!  * + Anonymous_NoUserId		[ on | off ] default = off
!  * + Anonymous_Authorative        [ on | off ] default = off
!  * + Anonymous_LogFile		< filename >
!  *
!  * (Legend -: Can be used in access.conf, + can be used in srm.conf and
!  *            access.conf for local/server-wide scope)
   *
   * The magic user id is something like 'anonymous', it is NOT case sensitive. 
   * 
***************
*** 87,92 ****
--- 91,100 ----
   * in broken GUIs like W95 is often given by the user. The Default is off.
   *
   * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx
+  *
+  * Changes 0.5 Release
+  *	   0.6 added separate logging
+  *	   0.7 Made it pass -Wall, removed empty auth function
   * 
   */
  
***************
*** 96,101 ****
--- 104,129 ----
  #include "http_log.h"
  #include "http_protocol.h"
  
+ /* Because the server-config is not know to the directory config
+  * creation routine, we cannot copy the global defaults along.
+  * This routine allows the global settings to override the local
+  * ones if they are not set.
+  */
+ #define ISSET(local,global,x) (((local->x != UNSET) && (local->x==YES)) ||
((local->x == UNSET) && (global->x==YES)))
+ #define NOSET( local,global,x) (((local->x != UNSET) && (local->x==NO)) ||
((local->x == UNSET) && (global->x==NO)))
+ 
+ #define YES (1)
+ #define NO (0)
+ #define UNSET (-1)
+ 
+ #define NOT_OPEN (-1)
+ 
+ module anon_auth_module;
+ 
+ /* This is just a boring linked list containing
+  * all possible anonymous userID's
+  */
+ 
  typedef struct auth_anon {
      char *password;
      struct auth_anon * next;
***************
*** 104,224 ****
  typedef struct  {
  
      auth_anon *auth_anon_passwords;
      int   auth_anon_nouserid;
      int   auth_anon_logemail;
      int   auth_anon_verifyemail;
      int   auth_anon_mustemail;
      int   auth_anon_authorative;
- 
  } anon_auth_config_rec;
  
! void *create_anon_auth_dir_config (pool *p, char *d)
! {
      anon_auth_config_rec * sec = (anon_auth_config_rec *) 
  	pcalloc (p, sizeof(anon_auth_config_rec));
  
!     if (!sec) return NULL; /* no memory... */
  
!     /* just to illustrate the defaults really. */
!     sec -> auth_anon_passwords 		=NULL;
  
      sec -> auth_anon_nouserid 		=0;
      sec -> auth_anon_logemail		=1;
      sec -> auth_anon_verifyemail	=0;
      sec -> auth_anon_mustemail		=1;
      sec -> auth_anon_authorative        =0;
      return sec;
! }
  
  char *anon_set_passwd_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_mustemail=arg;
      return NULL;
  }
  
  char *anon_set_userid_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_nouserid=arg;
      return NULL;
  }
  char *anon_set_logemail_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_logemail=arg;
      return NULL;
  }
  char *anon_set_verifyemail_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_verifyemail=arg;
      return NULL;
  }
  char *anon_set_authorative_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_authorative=arg;
      return NULL;
  }
  
  char *anon_set_string_slots (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, char *arg) {
    
      auth_anon 	* first;
  
      if (!(*arg))
!       return "Anonymous string cannot be empty, use Anonymous_NoUserId instead";
  
      /* squeeze in a record */
      first = sec->auth_anon_passwords;
!        
      if (
  	(!(sec->auth_anon_passwords=(auth_anon *) palloc(cmd -> pool, sizeof(auth_anon))))
||
  	(!(sec->auth_anon_passwords->password = pstrdup(cmd -> pool, arg)))
         ) return "Failed to claim memory for an anonymous password...";
  
-     /* and repair the next */
      sec->auth_anon_passwords->next = first;
  
      return NULL;
  }
  
  command_rec anon_auth_cmds[] = {
  { "Anonymous", anon_set_string_slots,
!     NULL,OR_AUTHCFG, ITERATE, NULL },
! { "Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_AUTHCFG, FLAG, 
! 	"Limited to 'on' or 'off'" },
! { "Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_AUTHCFG, FLAG, 
! 	"Limited to 'on' or 'off'" },
! { "Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_AUTHCFG, FLAG, 
! 	"Limited to 'on' or 'off'" },
! { "Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_AUTHCFG, FLAG, 
! 	"Limited to 'on' or 'off'" },
! { "Anonymous_Authorative", anon_set_authorative_flag, NULL, OR_AUTHCFG, FLAG, 
! 	"Limited to 'on' or 'off'" },
  
  { NULL }
  };
  
! module anon_auth_module;
  
  int anon_authenticate_basic_user (request_rec *r)
  {
      anon_auth_config_rec *sec =
        (anon_auth_config_rec *)get_module_config (r->per_dir_config,
  						&anon_auth_module);
      conn_rec *c = r->connection;
      char *send_pw;
      char errstr[MAX_STRING_LEN];
      int res=DECLINED;
      
- 
      if ((res=get_basic_auth_pw (r,&send_pw)))
! 	return res;
  
      /* Ignore if we are not configured */
      if (!sec->auth_anon_passwords) return DECLINED;
  
      /* Do we allow an empty userID and/or is it the magic one
!      */
!     
!     if ( (!(c->user[0])) && (sec->auth_anon_nouserid) ) {
        res=OK;
      } else {
        auth_anon *p=sec->auth_anon_passwords;
--- 132,431 ----
  typedef struct  {
  
      auth_anon *auth_anon_passwords;
+ 
+     char  *auth_anon_logfile;
+     int   auth_anon_fd;
+ 
      int   auth_anon_nouserid;
      int   auth_anon_logemail;
      int   auth_anon_verifyemail;
      int   auth_anon_mustemail;
      int   auth_anon_authorative;
  } anon_auth_config_rec;
  
! 
! void *create_config (pool *p) {
      anon_auth_config_rec * sec = (anon_auth_config_rec *) 
  	pcalloc (p, sizeof(anon_auth_config_rec));
  
!     return sec;
! }
  
! void *create_anon_server_config (pool *p, server_rec *s) {
! 
!     anon_auth_config_rec * sec = (anon_auth_config_rec *) 
! 	pcalloc (p, sizeof(anon_auth_config_rec));
  
+     /* just to illustrate the defaults really. */
+     sec -> auth_anon_fd			=NOT_OPEN;
      sec -> auth_anon_nouserid 		=0;
      sec -> auth_anon_logemail		=1;
      sec -> auth_anon_verifyemail	=0;
      sec -> auth_anon_mustemail		=1;
      sec -> auth_anon_authorative        =0;
+ 
+     sec -> auth_anon_passwords 		=NULL;
+     sec -> auth_anon_logfile 		=NULL;
      return sec;
!     };
! 
! void *create_anon_auth_dir_config (pool *p, char *d) {
!     anon_auth_config_rec * sec = (anon_auth_config_rec *) 
! 	pcalloc (p, sizeof(anon_auth_config_rec));
! 
!     /* just to illustrate the defaults really. */
!     sec -> auth_anon_fd			=NOT_OPEN;
!     sec -> auth_anon_nouserid 		=UNSET;
!     sec -> auth_anon_logemail		=UNSET;
!     sec -> auth_anon_verifyemail	=UNSET;
!     sec -> auth_anon_mustemail		=UNSET;
!     sec -> auth_anon_authorative        =UNSET;
! 
!     sec -> auth_anon_passwords 		=NULL;
!     sec -> auth_anon_logfile 		=NULL;
!     return sec;
!     };
! 
! 
! /* These really should be replaced by a generic set_flag_slot()
!  * (like a set_string_slot() in the http_config.c
!  */
! int valid_flag_value(int x) {
!     if (x) return YES;
!     return NO;
!     };
  
  char *anon_set_passwd_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_mustemail=valid_flag_value(arg);
      return NULL;
  }
  
  char *anon_set_userid_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_nouserid=valid_flag_value(arg);
      return NULL;
  }
  char *anon_set_logemail_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_logemail=valid_flag_value(arg);
      return NULL;
  }
  char *anon_set_verifyemail_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_verifyemail=valid_flag_value(arg);
      return NULL;
  }
  char *anon_set_authorative_flag (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, int arg) {
!     sec->auth_anon_authorative=valid_flag_value(arg);
!     return NULL;
! }
! 
! char *anon_set_anon_logfile (cmd_parms *cmd, 
! 	anon_auth_config_rec *sec, char *arg) {
! 
!     /* Endorsed 'HACK' to find out wether we are talking
!      * about the server as a whole (srm.conf) or just about
!      * a directory in access.conf.
!      */
!     if (!(cmd->path))
! 	sec=(anon_auth_config_rec *) get_module_config(cmd->server->module_config,&anon_auth_module);
! 
!     sec->auth_anon_logfile=arg;
! 
      return NULL;
  }
  
+ /* This routine is called ITERating, and the escaping and " removal
+  * is already done for us.
+  */
  char *anon_set_string_slots (cmd_parms *cmd, 
  	anon_auth_config_rec *sec, char *arg) {
    
      auth_anon 	* first;
  
      if (!(*arg))
!       return "An Anonymous string cannot be empty, use Anonymous_NoUserId instead";
  
      /* squeeze in a record */
      first = sec->auth_anon_passwords;
! 
      if (
  	(!(sec->auth_anon_passwords=(auth_anon *) palloc(cmd -> pool, sizeof(auth_anon))))
||
  	(!(sec->auth_anon_passwords->password = pstrdup(cmd -> pool, arg)))
         ) return "Failed to claim memory for an anonymous password...";
  
      sec->auth_anon_passwords->next = first;
  
      return NULL;
  }
  
+ 
+ 
  command_rec anon_auth_cmds[] = {
  { "Anonymous", anon_set_string_slots,
!     NULL,OR_AUTHCFG, ITERATE, 
! 	"Magic 'anonymous' username" 
! 	},
! { "Anonymous_LogFile", anon_set_anon_logfile,
!     NULL,OR_ALL, TAKE1, 
! 	"logfile for anonymous 'email/passwords'"
! 	},
! { "Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_ALL, FLAG, 
! 	"Limited to 'on' or 'off', enforce that the user must give a password" 
! 	},
! { "Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_ALL, FLAG, 
! 	"Limited to 'on' or 'off', accept an empty userID string" 
! 	},
! { "Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_ALL, FLAG, 
! 	"Limited to 'on' or 'off', verify email addresses" 
! 	},
! { "Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_ALL, FLAG, 
! 	"Limited to 'on' or 'off', logs the email addresses" 
! 	},
! { "Anonymous_Authorative", anon_set_authorative_flag, NULL, OR_ALL, FLAG, 
! 	"Limited to 'on' or 'off', makes this module authorative" 
! 	},
  
  { NULL }
  };
  
! 
! void anonymous_log_child (void *cmd)
! {
!     /* Child process code for 'AgentLog "|..."';
!      * may want a common framework for this, since I expect it will
!      * be common for other foo-loggers to want this sort of thing...
!      */
!     
!     cleanup_for_exec();
!     signal (SIGHUP, SIG_IGN);
!     execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
!     exit (1);
! }
! 
! int open_anonymous_log (anon_auth_config_rec *sec, pool *p)
! {
!     char *filename;
! 
!     /* Error handling is a bit peculiar; the routine is called
!      * during an init, at which the error is printed on stderr
!      * and it should exit(0) the server, and during run time
!      * when it is up to the auth funct to do the error reporting.
!      */
!    if (!(sec->auth_anon_logfile))
! 	return 1;
! 
!     filename = server_root_relative (p, sec->auth_anon_logfile);
! 
!     if (sec->auth_anon_fd != NOT_OPEN) return 1; /* virtual log shared w/main server
*/
!     
!     if (*sec->auth_anon_logfile == '|') {
! 	FILE *pipe;
! 	
! 	spawn_child(p, anonymous_log_child, (void *)(sec->auth_anon_logfile+1),
! 		    kill_after_timeout, &pipe, NULL);
! 
! 	if (pipe == NULL) {
!             sec->auth_anon_fd=NOT_OPEN;
! 	    fprintf (stderr, "Couldn't fork child for Anonymous Logging process\n");
! 	    return 0;
! 	}
! 	sec->auth_anon_fd = fileno (pipe);
!     }
! 
!     else if(*sec->auth_anon_logfile != '\0') {
!       if((sec->auth_anon_fd = popenf(p, filename, 
! 		( O_WRONLY | O_APPEND | O_CREAT ), 
! #ifdef __EMX__
! 		( S_IREAD | S_IWRITE )
! #else
! 		( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH )
! #endif
! 		)) < 0) {
!         sec->auth_anon_fd=NOT_OPEN;
! 	fprintf(stderr,"httpd: could not open anonymous log file %s.\n", filename);
!         perror("open");
!         return 0;
!       }
!     }
!  return 1;
! }
! 
! int anonymous_log_transaction(request_rec *r,char *email)
! {
!     anon_auth_config_rec *sec =
!       (anon_auth_config_rec *)get_module_config (r->per_dir_config,
! 						&anon_auth_module);
!     anon_auth_config_rec *pec =
!       (anon_auth_config_rec *)get_module_config (r->server->module_config,
! 						&anon_auth_module);
!     pool *p = r->pool;
!     char sign,str[MAX_STRING_LEN];
!     long timz;
!     struct tm *t;
! 
!     /* where is the filename/file_fd ? */
!     if (!sec->auth_anon_logfile)
! 	sec=pec;
!  
!     /* do we really want logging ? */
!     if (!(sec->auth_anon_logfile))
!       	return OK;
!     
!     /* is there an Email address to log ? */
!     if (!(*email)) 
! 	return OK;
! 
!     /* do we need to (re-) open the file ? */
!    if (sec->auth_anon_fd==NOT_OPEN) {
! 	if (!(open_anonymous_log (sec,p))) {
! 		sprintf(str,"Anonymous: Could not open my logfile '%s': %s",
! 			sec->auth_anon_logfile,strerror(errno) 
! 			);
! 		log_error (str, r->server );
! 		return 0;
! 		};
! 	};
! 
!     t = get_gmtoff(&timz);
!     sign = (timz < 0 ? '-' : '+');
!     if(timz < 0) 
!         timz = -timz;
! 
!     strftime(str,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t);
! 
!     sprintf(&str[strlen(str)], "%c%02ld%02ld] %s\n", sign, timz/3600,
! 		timz%3600, email);
! 
!     return (int) ( strlen(str) == write(sec->auth_anon_fd, str, strlen(str)) );
!    }
! 
  
  int anon_authenticate_basic_user (request_rec *r)
  {
      anon_auth_config_rec *sec =
        (anon_auth_config_rec *)get_module_config (r->per_dir_config,
  						&anon_auth_module);
+     anon_auth_config_rec *pec =
+       (anon_auth_config_rec *)get_module_config (r->server->module_config,
+ 						&anon_auth_module);
      conn_rec *c = r->connection;
      char *send_pw;
      char errstr[MAX_STRING_LEN];
      int res=DECLINED;
      
      if ((res=get_basic_auth_pw (r,&send_pw)))
! 	return DECLINED;
  
      /* Ignore if we are not configured */
      if (!sec->auth_anon_passwords) return DECLINED;
  
+ 
      /* Do we allow an empty userID and/or is it the magic one
!      */    
!     if ( (!(c->user[0])) && (ISSET(sec,pec,auth_anon_nouserid))) {
        res=OK;
      } else {
        auth_anon *p=sec->auth_anon_passwords;
***************
*** 227,296 ****
  	if (!(strcasecmp(c->user,p->password)))
  	  res=OK;
  	p=p->next;
!       }
!     }
      if (
! 	/* username is OK */
! 	(res == OK) &&
! 	/* password been filled out ? */ 
! 	( (!sec->auth_anon_mustemail) || strlen(send_pw)  ) &&
! 	/* does the password look like an email address ? */
! 	( (!sec->auth_anon_verifyemail) || 
  	     (strpbrk("@",send_pw) != NULL) || 
  	     (strpbrk(".",send_pw) != NULL) 
  	  ) 
  	) {
!       if (sec->auth_anon_logemail) {
! 	sprintf(errstr,"Anonymous: Passwd <%s> Accepted", 
  			send_pw ? send_pw : "\'none\'");
! 	log_error (errstr, r->server );
!       }
        return OK;
!     } else {
!         if (sec->auth_anon_authorative) {
  	sprintf(errstr,"Anonymous: Authorative, Passwd <%s> not accepted",
! 		send_pw ? send_pw : "\'none\'");
  	log_error(errstr,r->server);
  	return AUTH_REQUIRED;
! 	}
! 	/* Drop out the bottom to return DECLINED */
!     }
!      
  
!    return DECLINED;
  }
      
! int check_anon_access (request_rec *r) {
! 
! #ifdef NOTYET
!     conn_rec *c = r->connection;
!     anon_auth_config_rec *sec =
!       (anon_auth_config_rec *)get_module_config (r->per_dir_config,
! 						&anon_auth_module);
! 	
!     if (!sec->auth_anon) return DECLINED;
! 
!     if ( strcasecmp(r->connection->user,sec->auth_anon ))
!      	return DECLINED;
  
!    return OK;
! #endif
!    return DECLINED;
! }
!  
  
  module anon_auth_module = {
     STANDARD_MODULE_STUFF,
!    NULL,			/* initializer */
     create_anon_auth_dir_config,	/* dir config creater */
     NULL,			/* dir merger ensure strictness */
!    NULL,			/* server config */
     NULL,			/* merge server config */
     anon_auth_cmds,		/* command table */
     NULL,			/* handlers */
     NULL,			/* filename translation */
     anon_authenticate_basic_user,/* check_user_id */
!    check_anon_access,		/* check auth */
     NULL,			/* check access */
     NULL,			/* type_checker */
     NULL,			/* fixups */
--- 434,521 ----
  	if (!(strcasecmp(c->user,p->password)))
  	  res=OK;
  	p=p->next;
!       };
!     };
!     /* Bail out if it is not our duty... */
!     if (res != OK) {
!   	if (sec->auth_anon_authorative) {
! 		sprintf(errstr,"Anonymous: Authorative, Username <%s> not accepted as anonymous",
! 			c->user ? c->user : "\'none-given\'");
! 		log_error(errstr,r->server);
! 		return AUTH_REQUIRED;
! 		};
! 	return DECLINED;
! 	};
! 
!     /* Hairy policy check, for just an email string, or one which 
!      * actually has at least a '@' and a '.' if so configured.
!      */
      if (
! 	( (NOSET(sec,pec,auth_anon_mustemail)) || strlen(send_pw)  ) &&
! 	( (NOSET(sec,pec,auth_anon_verifyemail)) || 
  	     (strpbrk("@",send_pw) != NULL) || 
  	     (strpbrk(".",send_pw) != NULL) 
  	  ) 
  	) {
! 
!       if (ISSET(sec,pec,auth_anon_logemail)) {
!         /* Is there a log file specified, or can we use the 
!          * normal error log ? 
!          */
!       	if ((sec->auth_anon_logfile)||(pec->auth_anon_logfile)) {
! 		if (!(anonymous_log_transaction(r,send_pw))) {
! 			sprintf(errstr,"Anonymous: Could not write to my logfile '%s': %s",
! 				sec->auth_anon_logfile,strerror(errno) 
! 				);
! 			log_error (errstr, r->server );
! 			};
! 	        } else {
! 		/* Or fall back on the normal one ? */
! 		sprintf(errstr,"Anonymous: Passwd <%s> Accepted", 
  			send_pw ? send_pw : "\'none\'");
! 		log_error (errstr, r->server );
!       		};
!           };
! 
        return OK;
!       };
! 
!    if (sec->auth_anon_authorative) {
  	sprintf(errstr,"Anonymous: Authorative, Passwd <%s> not accepted",
! 			send_pw ? send_pw : "\'none\'");
  	log_error(errstr,r->server);
  	return AUTH_REQUIRED;
! 	};
  
!     return DECLINED;
  }
      
! /* Just open them right away, so that the user gets the
!  * error messages during init, rather then when it is too
!  * late do do anything about them. 
!  */
  
! void init_anon_log ( server_rec *s, pool *p) {
!   for( ; s ; s = s->next ) {
! 	anon_auth_config_rec * cf= (anon_auth_config_rec *)
! 	    get_module_config(s->module_config,&anon_auth_module);
! 	if (!(open_anonymous_log ( cf , p ))) 
! 		exit(0);
! 	};
!   };
  
  module anon_auth_module = {
     STANDARD_MODULE_STUFF,
!    init_anon_log,		/* initializer */
     create_anon_auth_dir_config,	/* dir config creater */
     NULL,			/* dir merger ensure strictness */
!    create_anon_server_config,	/* server config */
     NULL,			/* merge server config */
     anon_auth_cmds,		/* command table */
     NULL,			/* handlers */
     NULL,			/* filename translation */
     anon_authenticate_basic_user,/* check_user_id */
!    NULL,			/* check auth */
     NULL,			/* check access */
     NULL,			/* type_checker */
     NULL,			/* fixups */



Mime
View raw message