��������� ��������: 
        1) � postgresql ������ ���������� ��� � ��� ������ /dev/hands :-)
        2) � mysql ��� ��������
������� �������� ����������:

0) ����� ���������� ./configure
1) ��������� �������� � src/modules
2) � src/modules/Makefile ��������:
all:    .........
        passwd_pgsql.so \
        passwd_mysql.so \
        .........

passwd_pgsql.so: passwd_sql.c ../modules.h ../oops.h
        ${CC} ${CFLAGS} ${CDEFS} -D__PGSQL -c passwd_sql.c -o
passwd_pgsql.o 
        ${LD} $(SOFLAGS) $(LDFLAGS) $(LIBS) -o passwd_pgsql.so
passwd_pgsql.o /usr/lib/libpq.a

passwd_mysql.so: passwd_sql.c ../modules.h ../oops.h
        ${CC} ${CFLAGS} ${CDEFS} -D__MYSQL -c passwd_sql.c -o
passwd_mysql.o
        ${LD} $(SOFLAGS) $(LDFLAGS) $(LIBS) -o passwd_mysql.so
passwd_mysql.o /usr/lib/mysql/libmysqlclient.a

3) � ����� src/modules.c � ������� load_modules(void) ��������
��������������� ������ �� �������:
    insert_module(&passwd_pgsql.general, (struct general_module
**)&auth_first);
    insert_module(&passwd_mysql.general, (struct general_module
**)&auth_first);

4) ����������� oops

5) � oops.cfg ��������:
module passwd_pgsql {
        # �� ��� ������� ����� ����� ��� ������
        scheme          Basic
        realm           oops
        # ����� ������� � ���/������ ��� �������
        host            <host address/name>
        user            <database_user>
        password        <user_password>
        # ��� ����
        database        <database_name>
        # ���� � ������� ����������� ������ � ����� ����
        # ������ ���������� _�������_ � ����� ��������� login|passwd
        select          /usr/local/oops/select.sql
        # ��� �������� � ������ ������������ ������ :-)
        template        /usr/local/oops/auth_template.html
}

module passwd_mysql {
        # �� �������� � passwd_pgsql
}

6) ���������� � ���������... :-))))
�����! ���� �������� � �����������. Bugs wellcome!


-- 
Dixi.
Bah.                    mail-to: [EMAIL PROTECTED]
/*
Copyright (C) 1999 Igor Khasilev,   [EMAIL PROTECTED]
Copyright (C) 2000 Vladimir Pivkin, [EMAIL PROTECTED]

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.
*/

#include        "../oops.h"
#include        "../modules.h"

#if defined(__PGSQL)
    #include        <pgsql/libpq-fe.h>
    #define     MODULE_NAME             "passwd_pgsql"
    #define     MODULE_STRUCT_NAME      passwd_pgsql
    #define     MODULE_INFO             "Auth using postgres"
#elif defined(__MYSQL)
    #include        <mysql/mysql.h> 
    #define     MODULE_NAME             "passwd_mysql"
    #define     MODULE_STRUCT_NAME      passwd_mysql
    #define     MODULE_INFO             "Auth using mysql"
#else
    #error You mast select & predefine your sql server
#endif

#if     defined(MODULES)
#define MODULE_STATIC
#else
#define MODULE_STATIC static
#endif

MODULE_STATIC   char            module_type   = MODULE_AUTH ;
MODULE_STATIC   char            module_name[] = MODULE_NAME ;
MODULE_STATIC   char            module_info[] = MODULE_INFO ;
MODULE_STATIC   int             mod_load(),mod_unload();
MODULE_STATIC   int             mod_config_beg(), mod_config_end(), mod_config(), 
mod_run();
MODULE_STATIC   int             auth(int so, struct group *group, struct request* rq, 
int *flags);

struct  auth_module MODULE_STRUCT_NAME = {
        {
        NULL, NULL,
        MODULE_NAME,
        mod_load,
        mod_unload,
        mod_config_beg,
        mod_config_end,
        mod_config,
        NULL,
        MODULE_AUTH,
        MODULE_INFO,
        mod_run
        },
        auth
};

static  rwl_t   pwf_lock;
static  char    pwf_name[MAXPATHLEN]="";
static  time_t  pwf_mtime=0,pwf_check_time=0;
static  char    pwf_charset[64]="";
static  char    realm[64]="";
static  enum    {Basic,Digest} scheme = Basic;

typedef struct {
  char          filename[MAXPATHLEN];
  time_t        mtime;
  time_t        check_time;
  int           len;
  char          *data;
} PWF_FILEINFO;

static PWF_FILEINFO template={"",0,0,0,0};
static PWF_FILEINFO sqlselect={"",0,0,0,0};

typedef struct {
  char          host[32];
  char          user[32];
  char          password[32];
  char          database[32];
} SQL_INFO;

SQL_INFO sql={"localhost","","",""};

typedef struct {
  char name[20];
  char pass[20];
} USERS_INFO;

USERS_INFO *userlist=NULL;
int userlist_count=0;

static  time_t  sqlres_check_time;
static  rwl_t   sql_lock;

static  char    *authreq = NULL;
static  int      authreqlen;
static  char    *authreqfmt = "%s realm=%s";
static  char    *std_template = 
              "\n<body>Authorization to proxy-server failed.<p><hr>\n"
              "<i><font size=-1>by \'passwd_pgsql\' module to Oops.";
static  int     std_template_len;
static  int     pwf_charset_len;
static  int     badschlen;
static  char    *badsch=NULL;
static  char    *badschfmt =
               "HTTP/1.0 407 Proxy Authentication required\n"
               "Proxy-Authenticate: %s realm=%s\n\n"
               "<body>Authorization to proxy-server failed.<p>\n"
               "Your browser proposed unsupported scheme\n"
               "<hr>\n"
               "<i><font size=-1>by \'passwd_file\' module to Oops.";
static char *logbuf=NULL;

#define RDLOCK_PWF_CONFIG       rwl_rdlock(&pwf_lock)
#define WRLOCK_PWF_CONFIG       rwl_wrlock(&pwf_lock)
#define UNLOCK_PWF_CONFIG       rwl_unlock(&pwf_lock)


#define RDLOCK_SELECT   rwl_rdlock(&sql_lock)
#define WRLOCK_SELECT   rwl_wrlock(&sql_lock)
#define UNLOCK_SELECT   rwl_unlock(&sql_lock)

static void     check_age_and_reload(void);
static void     reload_pwf_file(PWF_FILEINFO *fi);
static void     make_sqlselect(void);

static  int     auth_check_user(char * user, char * pass);
static  void    send_auth_req(int, struct request *);

// ------------------------------------------------------------------------
int mod_run()
{
    return(MOD_CODE_OK);
}

int mod_load()
{
    printf(MODULE_NAME "started\n");
    rwl_init(&pwf_lock);
    rwl_init(&sql_lock);
    std_template_len = strlen(std_template);
    logbuf=malloc(8*1024);

    return(MOD_CODE_OK);
}
// ------------------------------------------------------------------------
int mod_unload()
{
    if (logbuf)         free(logbuf);
    if (template.data)  free(template.data);
    if (sqlselect.data) free(sqlselect.data);
    if (userlist)       free(userlist);
    printf(MODULE_NAME " stopped\n");
    return(MOD_CODE_OK);
}
// ------------------------------------------------------------------------
int mod_config_beg()
{
    WRLOCK_PWF_CONFIG ;
    if ( authreq )        free(authreq);  authreq = 0;
    if ( badsch )         free(badsch);   badsch = 0;
    if ( template.data )  free(template.data); template.data = 0;
    if ( sqlselect.data ) free(sqlselect.data); sqlselect.data = 0;

    pwf_name[0] = template.filename[0] = sqlselect.filename[0] = 0;
    pwf_charset[0]      = 0;
    pwf_mtime = template.mtime = sqlselect.mtime =0;
    scheme = Basic;
   
    pwf_check_time=sqlres_check_time =0;

    strcpy(sql.host,"localhost");
    sql.user[0]=sql.password[0]=sql.database[0]=0;
    
    UNLOCK_PWF_CONFIG ;
    return(MOD_CODE_OK);
}
// ------------------------------------------------------------------------
int mod_config_end()
{
char    *sch="None";

    WRLOCK_PWF_CONFIG ;
    if      ( scheme == Basic ) sch = "Basic";
    else if ( scheme == Digest) sch = "Digest";
    authreqlen = 0;
    authreq = malloc(strlen(authreqfmt)+1+strlen(realm)+strlen(sch));
    if ( authreq ) {
        sprintf(authreq, authreqfmt, sch,realm);
        authreqlen = strlen(authreq);
    }

    badschlen = 0;
    badsch = malloc(strlen(badschfmt)+1+strlen(realm)+strlen(sch));
    if ( badsch ) {
        sprintf(badsch, badschfmt, sch, realm);
        badschlen = strlen(badsch);
    }

    UNLOCK_PWF_CONFIG ;

    RDLOCK_PWF_CONFIG ;
    reload_pwf_file(&template);
    reload_pwf_file(&sqlselect);
    make_sqlselect();
    UNLOCK_PWF_CONFIG ;

    return(MOD_CODE_OK);
}
// ------------------------------------------------------------------------
int mod_config(char *config)
{
    char        *p = config;

    WRLOCK_PWF_CONFIG ;

    while( *p && IS_SPACE(*p) ) p++;
    if ( !strncasecmp(p, "host", 4) ) {
        p += 4;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(sql.host, p, sizeof(sql.host) -1 );
    } else if ( !strncasecmp(p, "user", 4) ) {
        p += 4;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(sql.user, p, sizeof(sql.user) -1 );
    } else if ( !strncasecmp(p, "password", 8) ) {
        p += 8;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(sql.password, p, sizeof(sql.password) -1 );
    } else if ( !strncasecmp(p, "database", 8) ) {
        p += 8;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(sql.database, p, sizeof(sql.database) -1 );
    } else if ( !strncasecmp(p, "select", 6) ) {
        p += 6;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(sqlselect.filename, p, sizeof(sqlselect.filename) -1 );
    } else if ( !strncasecmp(p, "template", 8) ) {
        p += 8;
        while (*p && IS_SPACE(*p) ) p++;
        strncpy(template.filename, p, sizeof(template.filename) -1 );
    } else if ( !strncasecmp(p, "charset", 7) ) {
        p += 7;
        while (*p && IS_SPACE(*p) ) p++;
        sprintf(pwf_charset, "Content-Type: text/html; charset=%.20s\n", p);
        pwf_charset_len = strlen(pwf_charset);
    } else if ( !strncasecmp(p, "scheme", 6) ) {
        p += 6;
        while (*p && IS_SPACE(*p) ) p++;
        if ( !strcasecmp(p, "basic") )  scheme = Basic;
        else if ( !strcasecmp(p, "digest") ) scheme = Digest;
    } 

    UNLOCK_PWF_CONFIG ;
    return(MOD_CODE_OK);
}


void check_age_and_reload(void)
{

    if ((global_sec_timer - template.check_time) >= 60 && template.filename[0]) {
      reload_pwf_file(&template);
    }
    if ((global_sec_timer - sqlselect.check_time) >= 60 && sqlselect.filename[0]) {
      reload_pwf_file(&sqlselect);
    }
    if ((global_sec_timer - sqlres_check_time) >= 60 ) {
      my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "auth():check_age&reload reload select");
      make_sqlselect();
      sqlres_check_time=global_sec_timer;
    }
    
}

void reload_pwf_file(PWF_FILEINFO *fi)
{
struct  stat    sb;
int             rc, size, fd;
    rc = stat(fi->filename, &sb);
    if ( rc != -1 ) {
        if ( sb.st_mtime <= fi->mtime ) return;
        size = sb.st_size;
        if ( size <= 0 ) return;
        if ( fi->data) free(fi->data); fi->data = NULL;
        fi->data = xmalloc(size+1,"reload_pwf_template(): 1");
        if ( fi->data ) {
            fd = open(fi->filename, O_RDONLY);
            if ( fd != -1 ) {
                rc = read(fd, fi->data, size);
                if ( rc != size ) {
                    free(fi->data);fi->data = NULL;
                } 
                else {
                    fi->mtime = sb.st_mtime;
                    fi->check_time = global_sec_timer;
                    fi->len = size;
                    fi->data[size]=0;    
                }
                close(fd);
            } else {
                free(fi->data); fi->data = NULL;
            }
        }
    }
}

void righttrim(char *str)
{ 
    int i;
    for (i=strlen(str);i>0 && str[i-1]==' ';i--) ;
    str[i]=0;
}

void make_sqlselect(void)
{
    int i;
#if defined(__PGSQL)
    PGconn      *conn=0
;
    PGresult    *res=0;
#elif defined(__MYSQL)
    MYSQL       *mysql;
    MYSQL_FIELD *fields;
    MYSQL_RES   *rs;
    MYSQL_ROW    row;
#endif
    int loginfield_n,passfield_n;

    if (!sqlselect.data) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Select not 
loaded\n");
        if (userlist) free(userlist);
        userlist=0;
        userlist_count=0;
        return; 
    }
    WRLOCK_SELECT;
    
#if defined(__PGSQL)
    conn=PQsetdbLogin(sql.host,"5432",NULL,NULL,
                        sql.database,sql.user,sql.password);

    if (!conn || PQstatus(conn) == CONNECTION_BAD)
 {
        sprintf(logbuf,"make_sqlselect(): Connection to database '%s' failed 
(error=%s)\n",
               sql.database,PQerrorMessage(conn)
        );
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, logbuf);
        goto exit;
    }

    res = PQexec(conn,sqlselect.data);

    if (res==0) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM,"make_sqlselect(): Result of select is 
null\n");
        PQfinish(conn);
        goto exit;
    } 
    if (PQresultStatus(res)!=PGRES_TUPLES_OK) 
{
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): select failed\n");
        goto pgsql_exit;
    }
    
    userlist_count=PQntuples(res);
    if (userlist_count) {

        loginfield_n=PQfnumber(res,"login");
        passfield_n=PQfnumber(res,"passwd");
        if (loginfield_n==-1 || passfield_n==-1) {
            my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error find field 
login,passwd inselect\n");
            goto pgsql_exit;
        }

        userlist=(USERS_INFO *)malloc(userlist_count*sizeof(USERS_INFO));    
        if (!userlist) {
            my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error allocate 
memory for userinfo\n");
            goto pgsql_exit;
        }

        for (i=0;i<userlist_count;i++) {
            strcpy(userlist[i].name,PQgetvalue(res,i,loginfield_n));
            righttrim(userlist[i].name);
            strcpy(userlist[i].pass,PQgetvalue(res,i,passfield_n));  
            righttrim(userlist[i].pass);
        } 
    }
pgsql_exit:    
    PQclear(res); 
    PQfinish(conn);

#elif defined(__MYSQL)
    
    mysql=mysql_init(NULL);
    if (!mysql) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error init mysql\n");
        goto exit;
    }

    if (!mysql_real_connect(mysql,sql.host,sql.user,sql.password,sql.database,
                                3306, 0, 0)) 
    {
        sprintf(logbuf,"make_sqlselect(): Connection to database '%s' failed 
(error=%s)\n",
                       sql.database,mysql_error(mysql)
               );
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, logbuf);
        
    }

    if (mysql_real_query(mysql, sqlselect.data, sqlselect.len)!=0) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): select failed\n");
        goto mysql_exit;
    }  
    
    rs = mysql_store_result(mysql);
    
    userlist_count=mysql_num_rows(rs);

    if (userlist_count) {
    
        fields=mysql_fetch_fields(rs);
        if (!fields) {
            my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error get fields 
info\n");
            goto mysql_free_rs_exit;
        }
        loginfield_n=passfield_n=-1;
        for (i=0;i<mysql->field_count;i++) {
          if      (strcmp(fields[i].name,"login")==0) loginfield_n=i;
          else if (strcmp(fields[i].name,"passwd")==0) passfield_n=i;
        }
        if (loginfield_n==-1 || passfield_n==-1) {
            my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error find field 
login,passwd inselect\n");
            goto mysql_free_rs_exit;
        }

        userlist=(USERS_INFO *)malloc(userlist_count*sizeof(USERS_INFO));    

        if (!userlist) {
            my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "make_sqlselect(): Error allocate 
memory for userinfo\n");
            goto mysql_free_rs_exit;
        }

        for (i=0;(i<userlist_count) && (row=mysql_fetch_row(rs));i++) {
            strcpy(userlist[i].name,row[loginfield_n]);
            righttrim(userlist[i].name);
            strcpy(userlist[i].pass,row[passfield_n]);  
            righttrim(userlist[i].pass);
        } 
        if (i<userlist_count)  userlist_count=i;

    }
mysql_free_rs_exit:
    mysql_free_result(rs);
    
mysql_exit:    
    
    mysql_close(mysql);
 
#endif
exit:
    UNLOCK_SELECT;
}

int auth(int so, struct group *group, struct request* rq, int *flags) 
{
    char        *authorization = NULL;

    if ( !authreq ) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "auth(): Something wrong with 
passwd_pgsql module.\n");
        return(MOD_CODE_OK);
    }

    if (!logbuf) return(MOD_CODE_ERR);

    sprintf(logbuf,"auth(): request: "
                 "ip=%d.%d.%d.%d host=%s method=%s\n",
                 (rq->client_sa.sin_addr.s_addr      ) & 0xff,
                 (rq->client_sa.sin_addr.s_addr >>  8) & 0xff,
                 (rq->client_sa.sin_addr.s_addr >> 16) & 0xff,
                 (rq->client_sa.sin_addr.s_addr >> 24) & 0xff,
                 rq->url.host,
                 rq->method
            );
    my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM,logbuf);

    if ( rq->av_pairs)
        authorization = attr_value(rq->av_pairs, "Proxy-Authorization");
        
    WRLOCK_PWF_CONFIG ;
    check_age_and_reload();
    UNLOCK_PWF_CONFIG;

    RDLOCK_PWF_CONFIG ;

    if ( !authorization ) {
        /* send 407 Proxy Authentication Required */
        goto au_f;
    } 

    if ( !strncasecmp(authorization, "Basic", 5 ) ) {
          int    rc;
          char  *u=NULL, *p;
          char  *data = authorization + 5;
            while ( *data && IS_SPACE(*data) ) data++;
            if ( *data ) u = base64_decode(data);
            if ( u ) {
                /* up = username:password */
                p = strchr(u, ':');
                if ( p ) { *p=0; p++; }
                if ( auth_check_user(u, p) ) {
                    IF_STRDUP(rq->proxy_user, u);
                    free(u);
                    goto au_ok;
                }
                free(u);
            } 
            goto au_f;
    } 
    else {
        /* we do not support any schemes except Basic */
        if ( badsch ) {
            writet(so, badsch, badschlen, 30);
            SET(*flags, MOD_AFLAG_OUT);
        }
        UNLOCK_PWF_CONFIG ;
        return(MOD_CODE_ERR);
    }    
au_f: 
    send_auth_req(so, rq);
    SET(*flags, MOD_AFLAG_OUT);
    UNLOCK_PWF_CONFIG ;
    return(MOD_CODE_ERR);
    
au_ok:
    UNLOCK_PWF_CONFIG ;
    return(MOD_CODE_OK);
}

int auth_check_user(char *user, char *pass)
{
    int i=0;
    if (!user || !pass) {
        my_xlog(LOG_NOTICE|LOG_DBG|LOG_INFORM, "auth_check_user(): Bad user or 
pass\n");
        return 0;
    }

    RDLOCK_SELECT;
    if (userlist) {

        while(i<userlist_count) {
            if (strcmp(userlist[i].name,user)==0 &&
                strcmp(userlist[i].pass,pass)==0
               ) { 
                UNLOCK_SELECT;
                return 1;  
            }
            i++;
        }
            
    }
    UNLOCK_SELECT;
    return 0;
}

void
send_auth_req(int so, struct request *rq)
{
struct  output_object   *obj;
struct  buff            *body;
int                     rc;

    obj = xmalloc(sizeof(*obj),"send_auth_req(): obj");
    if ( !obj )
        return;

    bzero(obj, sizeof(*obj));

    put_av_pair(&obj->headers,"HTTP/1.0", "407 Proxy Authentication Required");
    put_av_pair(&obj->headers,"Proxy-Authenticate:", authreq);
    put_av_pair(&obj->headers,"Content-Type:", "text/html");

    if ( !template.data ) body = alloc_buff(std_template_len);
    else                  body = alloc_buff(template.len);
    if ( body ) {
        obj->body = body;
        if ( !template.data )
                rc = attach_data(std_template, std_template_len, body);
            else
                rc = attach_data(template.data, template.len, body);
        if ( !rc )
                process_output_object(so, obj, rq);
    }

    free_output_obj(obj);
    return;
}

Дати відповідь електронним листом