src/midgard.c

00001 /* $Id: midgard.c,v 1.137 2006/10/12 11:07:07 piotras Exp $
00002  *
00003  * midgard-lib: database access for Midgard clients
00004  *
00005  * Copyright (C) 1999 Jukka Zitting <jukka.zitting@iki.fi>
00006  * Copyright (C) 2000 The Midgard Project ry
00007  * Copyright (C) 2003 David Schmitter, Dataflow Solutions GmbH <schmitt@dataflow.ch>
00008  * Copyright (C) 2003-2004 Alexander Bokovoy <ab@altlinux.org>
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Library General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Library General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this library; see the file COPYING.  If not, write to
00022  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00023  * Boston, MA 02111-1307, USA.
00024  */
00025 
00026 #include <config.h>
00027 #include "midgard/midgard_connection.h"
00028 #include "midgard/authfailure.h"
00029 #include "defaults.h"
00030 #include "fmt_russian.h"
00031 #include "midgard/midgard_quota.h"
00032 #if HAVE_CRYPT_H
00033 # include <crypt.h>
00034 #else
00035 # define _XOPEN_SOURCE
00036 #endif
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <stdio.h>
00040 #ifdef WIN32
00041 # include <windows.h>
00042 #endif
00043 #include <glib.h>
00044 #include <ctype.h>
00045 #ifndef WIN32
00046 # include <unistd.h>
00047 #endif
00048 #include <signal.h>
00049 
00050 #ifdef WIN32
00051 # include <process.h>
00052 # include "win32/flock.h"
00053 # include "win32/win95nt.h"
00054 #else
00055 # include <sys/utsname.h>
00056 # include <sys/file.h>
00057 # include <sys/wait.h>
00058 # include <netdb.h>
00059 #endif
00060 
00061 #include <sys/stat.h>
00062 
00063 #if HAVE_MIDGARD_VC
00064 # include <sys/time.h>
00065 #endif
00066 
00067 #ifndef MAXHOSTNAMELEN
00068 # define MAXHOSTNAMELEN 255
00069 #endif
00070 
00071 #ifndef G_LOG_DOMAIN
00072 #undef G_LOG_DOMAIN
00073 #define G_LOG_DOMAIN="midgard-core"
00074 #endif
00075 
00076 #include "midgard_mysql.h"
00077 #include "midgard/midgard_legacy.h"
00078 
00079 static FILE *_log_file = NULL;
00080 
00081 void mgd_log_debug_none(const gchar *domain, GLogLevelFlags level,
00082       const gchar *msg, gpointer userdata)
00083 {
00084 }
00085 
00086 void mgd_log_debug_default(const gchar *domain, GLogLevelFlags level,
00087       const gchar *msg, gpointer userdata)
00088 {
00089    int fno;
00090    guint mlevel = (guint) userdata;
00091    gchar *level_ad = NULL;
00092 
00093    switch (level) {
00094       case G_LOG_FLAG_RECURSION:
00095          level_ad =  "RECURSION"; 
00096          break;
00097 
00098       case G_LOG_FLAG_FATAL:
00099          level_ad = "FATAL! ";
00100          break;
00101 
00102       case G_LOG_LEVEL_ERROR:
00103          level_ad =  "ERROR";
00104          break;
00105 
00106       case G_LOG_LEVEL_CRITICAL:
00107          level_ad = "CRITICAL ";
00108          break;
00109 
00110       case G_LOG_LEVEL_WARNING:
00111          level_ad =  "WARNING";
00112          break;
00113 
00114       case G_LOG_LEVEL_MESSAGE:
00115          level_ad = "m";
00116          break;
00117 
00118       case G_LOG_LEVEL_INFO:
00119          level_ad = "info";
00120          break;
00121 
00122       case G_LOG_LEVEL_DEBUG:
00123          level_ad = "debug";
00124          break;
00125 
00126       default:
00127          /* Should never happen , default log level should be set 
00128           * in midgard_config_init when unknown defined */
00129          level_ad =  "Unknown level";
00130          break;
00131    }
00132 
00133    if (mlevel >= level) {
00134        
00135        if (_log_file == NULL) _log_file = stderr;
00136        fno = fileno(_log_file);
00137 
00138        if (fno != 2) flock(fno, LOCK_EX);
00139 
00140        fprintf(_log_file, "%s ", domain != NULL ? domain : "midgard-core");
00141        /* I am not sure if we need pid */
00142        fprintf(_log_file, "(pid:%ld):", (unsigned long)getpid()); 
00143    
00144        fprintf(_log_file, "(%s):", level_ad);
00145        fprintf(_log_file, "  %s\n", msg);
00146        
00147        fflush(_log_file);
00148        if (fno != 2) flock(fno, LOCK_UN);
00149    }
00150 }
00151 
00152 void mgd_init()
00153 {
00154    mgd_init_ex(0, NULL);
00155 }
00156 
00157 guint mgd_parse_log_levels(const char *levels)
00158 {
00159    const char *c;
00160    guint mask = 0;
00161    int level = 0;
00162 
00163    for (c = levels; *c != '\0'; ) {
00164       /* skip over non-text */
00165       if (!isalpha(*c)) { c++; continue; }
00166 
00167       /* first char determines log level */
00168       switch (tolower(*c)) {
00169          /*[eeh]  I don't think we're supposed to offer these as log
00170           * level flags
00171             case 'r': level = G_LOG_FLAG_RECURSION; break;
00172             case 'f': level = G_LOG_FLAG_FATAL; break;
00173          */
00174          case 'e': level = G_LOG_LEVEL_ERROR; break;
00175          case 'c': level = G_LOG_LEVEL_CRITICAL; break;
00176          case 'w': level = G_LOG_LEVEL_WARNING; break;
00177          case 'm': level = G_LOG_LEVEL_MESSAGE; break;
00178          case 'i': level = G_LOG_LEVEL_INFO; break;
00179          case 'd': level = G_LOG_LEVEL_DEBUG; break;
00180          default: level = 4; break;
00181       }
00182 
00183       /* skip over rest of 'word' */
00184       for (c++; *c != '\0' && isalpha(*c); c++) {}
00185 
00186       /* last word was garbage, ignore, maybe we should log this */
00187       if (level == 0) continue;
00188 
00189       /* select single log level */
00190       if (*c == '+') {
00191          mask |= level;
00192          continue;
00193       }
00194 
00195       /* select loglevel and all higher loglevels */
00196       for (; level && level > G_LOG_FLAG_FATAL; level >>= 1) {
00197          mask |= level;
00198       }
00199       
00200    }
00201 
00202    return mask;
00203 }
00204 
00205 void mgd_init_ex(guint log_levels, char *logname)
00206 {
00207         if (mgd_parser_list())
00208                 return;
00209         /* MySQL expects utf8 instead of utf-8. 
00210          * latin1 instead of iso-8859-1.
00211          */     
00212         (void) mgd_parser_create("latin1", "latin1", 1);
00213         (void) mgd_parser_init_raw("russian", "KOI8-R");
00214         (void) mgd_parser_init_raw("utf-8", "UTF-8");
00215 
00216         if (logname == NULL) {
00217                 _log_file = stderr;
00218         } else {
00219                 _log_file = fopen(logname, "a");
00220                 if (_log_file == NULL) {
00221                         _log_file = stderr;
00222                         g_log("midgard-core", G_LOG_LEVEL_WARNING,
00223                                         "Could not open logfile '%s', using stderr", logname);
00224                 }
00225         }
00226 }
00227 
00228 void mgd_done()
00229 {
00230         mgd_parser_free_all();
00231 }
00232 
00233 const char *mgd_version()
00234 {
00235         return MIDGARD_LIB_VERSION;
00236 }
00237 
00238 midgard *mgd_setup()
00239 {
00240         midgard *mgd;
00241         /* create midgard handle */
00242         mgd = g_new(midgard, 1);
00243         if (!mgd)
00244                 return NULL;
00245 
00246         mgd->res = NULL;
00247 
00248         mgd->pool = mgd_alloc_pool();
00249         if (!mgd->pool) {
00250                 free(mgd);
00251                 return NULL;
00252         }
00253 
00254         mgd->tmp = mgd_alloc_pool();
00255         if (!mgd->tmp) {
00256                 mgd_free_pool(mgd->pool);
00257                 free(mgd);
00258                 return NULL;
00259         }
00260 
00261         mgd->current_user = &mgd->user_at_pagestart;
00262         
00263         mgd->user_at_pagestart.id = 0;
00264         mgd->setuid_user.id = 0;
00265         mgd->user_at_pagestart.is_admin = 0;
00266         mgd->setuid_user.is_admin = 0;
00267         mgd->user_at_pagestart.is_root = 0;
00268         mgd->setuid_user.is_root = 0;
00269         mgd->user_at_pagestart.sitegroup = 0;
00270         mgd->setuid_user.sitegroup = 0;
00271         mgd->user_at_pagestart.member_of = NULL;
00272         mgd->setuid_user.member_of = NULL;
00273 
00274         mgd->username = NULL;
00275 
00276         mgd->parser = mgd_parser_list(); 
00277 
00278         mgd->msql = g_new(midgard_mysql, 1);
00279         mgd->msql->mysql = NULL;
00280         mgd->blobdir = NULL;
00281 
00282         mgd->ah_prefix = NULL;
00283         mgd->auth_type = MGD_AUTHTYPE_NORMAL;
00284 
00285 #if HAVE_MIDGARD_MULTILANG
00286         mgd->lang = 0;
00287         mgd->default_lang = 0;
00288         mgd->a_lang = 0;
00289         mgd->p_lang = 0;
00290 #endif /* HAVE_MIDGARD_MULTILANG */
00291 
00292 #if HAVE_MIDGARD_VC
00293         mgd->cvs_script = NULL;
00294 #endif /* HAVE_MIDGARD_VC */
00295 
00296 #if HAVE_MIDGARD_QUOTA
00297         mgd->quota = 0;
00298         mgd->quota_err = 0;
00299         mgd->qlimits = g_hash_table_new(g_str_hash, g_str_equal);
00300 #endif /* HAVE_MIDGARD_QUOTA */
00301 
00302         mgd->loglevel = mgd_parse_log_levels("warn");
00303         mgd->logfile = NULL;
00304         mgd->loghandler = 0;
00305         mgd->person = NULL;
00306         mgd->schema = NULL;
00307         mgd->err = NULL;
00308         /* UGLY WORKAROUND TO AVOID LEGACY API USAGE */
00309         mgd->_mgd = midgard_connection_new();
00310         mgd->_mgd->mgd = mgd;   
00311         mgd->pamfile = "midgard";
00312         
00313         return mgd;
00314 }
00315 
00316 void mgd_set_authtype(midgard *mgd, midgard_auth_type authtype)
00317 {
00318         mgd->auth_type = authtype;
00319 }
00320 
00321 void mgd_easy_connect(midgard *mgd, const char *host, const char *database,
00322       const char *user, const char *password)
00323 {
00324         int connected;
00325 
00326         /* create mysql connection handle */
00327         mgd->msql->mysql = mysql_init(NULL);
00328 
00329         /* Set the correct character encoding, UTF-8 by default. Note */
00330         /* that Midgard uses "utf-8" where MySQL uses "utf8".         */
00331         if (mgd->msql->mysql != NULL) 
00332                 mysql_options(mgd->msql->mysql, MYSQL_SET_CHARSET_NAME, "utf8");
00333         
00334 
00335    /* connect to the database */
00336    connected = mgd->msql->mysql &&
00337       mysql_real_connect(mgd->msql->mysql, host,
00338          user, password, database, 0, NULL, 0);
00339 
00340         if (!mgd->msql->mysql || !connected) {
00341       /* EEH/TODO: I know this is ugly but we need this info.
00342          There will be proper logging at some point
00343       */
00344       g_warning(" %s@%s://%s connection failed: %s",
00345             user, host, database,
00346             mgd->msql->mysql ? mysql_error(mgd->msql->mysql) : "<mysql server not found?>");
00347 
00348                 if (mgd->msql->mysql) mysql_close(mgd->msql->mysql);
00349       mgd->msql->mysql = NULL;
00350    }
00351 }
00352 
00353 midgard *mgd_connect(const char *hostname, const char *database,
00354              const char *username, const char *password)
00355 {
00356 midgard *mgd = mgd_setup();
00357 
00358 if (mgd == NULL)
00359         return NULL;
00360 
00361 /* use default values if none specified */
00362 if (!hostname)
00363         hostname = MGD_MYSQL_HOST;
00364 if (!username)
00365         username = MGD_MYSQL_USERNAME;
00366 if (!password)
00367         password = MGD_MYSQL_PASSWORD;
00368 if (!database)
00369         database = MGD_MYSQL_DATABASE;
00370 
00371 /* create mysql connection handle */
00372 mgd_easy_connect(mgd, hostname, database, username, password);
00373 
00374 if (!mgd->msql->mysql) {
00375         mgd_free_pool(mgd->tmp);
00376         mgd_free_pool(mgd->pool);
00377         free(mgd);
00378         return NULL;
00379 }
00380 
00381 return mgd;
00382 }
00383 
00384 
00385 
00386 void mgd_close(midgard * mgd)
00387 {
00388 assert(mgd);
00389 mgd_clear(mgd, MGD_CLEAR_ALL);
00390 
00391 if (mgd->msql->mysql) {
00392         mysql_close(mgd->msql->mysql);
00393 mgd->msql->mysql = NULL;
00394 }
00395 
00396 if (mgd->user_at_pagestart.member_of != NULL)
00397 free(mgd->user_at_pagestart.member_of);
00398 if (mgd->setuid_user.member_of != NULL)
00399 free(mgd->setuid_user.member_of);
00400 
00401 mgd_free_pool(mgd->tmp);
00402 mgd_free_pool(mgd->pool);
00403 free(mgd);
00404 }
00405 
00406 
00407 int mgd_select_db(midgard * mgd, const char *database)
00408 {
00409 int errcode;
00410 
00411 assert(mgd);
00412 assert(database);
00413 
00414 if ((errcode = mysql_select_db(mgd->msql->mysql, database)) == 0) return 1;
00415 
00416 g_log("midgard-lib", G_LOG_LEVEL_ERROR,
00417  "Midgard: mgd_select_db(%s) failed: %s (%d)\n",
00418               database, mysql_error(mgd->msql->mysql), errcode);
00419 return 0;
00420 }
00421 
00422 int mgd_assert_db_connection(midgard *mgd, const char *hostname, const char *database,
00423 const char* username, const char *pass)
00424 {
00425 void (*handler) (int);
00426 
00427 handler=signal(SIGPIPE, SIG_IGN);
00428 
00429 /* EEH: ensure that the link did not die, from PHPs mysql connector */
00430 #if defined(CR_SERVER_GONE_ERROR)
00431 mysql_stat(mgd->msql->mysql);
00432 if (mysql_errno(mgd->msql->mysql) == CR_SERVER_GONE_ERROR) {
00433 #else
00434 if (!strcasecmp(mysql_stat(mgd->msql->mysql), "mysql server has gone away")) {
00435 #endif
00436         mysql_close(mgd->msql->mysql);
00437 sleep(1);
00438 mgd_easy_connect(mgd, hostname, database, username, pass);
00439 }
00440 
00441 signal(SIGPIPE, handler);
00442 return (mgd->msql->mysql != NULL);
00443 }
00444 
00445 #ifdef HAVE_LIBPAM
00446 
00447 #ifdef HAVE_SECURITY_PAM_APPL_H
00448 #include <security/pam_appl.h>
00449 #endif
00450 /* Mac OS X puts PAM headers into /usr/include/pam, not /usr/include/security */
00451 #ifdef HAVE_PAM_PAM_APPL_H
00452 #include <pam/pam_appl.h>
00453 #endif
00454 
00455 typedef struct {
00456 char *username;
00457 char *password;
00458 } _midgard_pam_appdata;
00459 
00460 static int _midgard_pam_conv(int num_msg, const struct pam_message **msg, 
00461                 struct pam_response **resp, void *appdata_ptr)
00462 {
00463         _midgard_pam_appdata *appdata = (_midgard_pam_appdata*) appdata_ptr;
00464         int i = 0, j = 0;
00465 
00466         if (num_msg && msg && *msg && resp && appdata_ptr) {
00467                 *resp = malloc(sizeof(struct pam_response)*num_msg);
00468                 if (*resp) {
00469                         /* Process conversation and fill in results */
00470                         for(; i < num_msg; i++) {
00471                                 (*resp)[i].resp_retcode = 0;
00472                                 (*resp)[i].resp = NULL;
00473                                 switch((*msg)[i].msg_style) {
00474                                         
00475                                         case PAM_PROMPT_ECHO_ON: /* username */
00476                                                 (*resp)[i].resp = strdup(appdata->username);
00477                                                 break;
00478                                         
00479                                         case PAM_PROMPT_ECHO_OFF: /* password */
00480                                                 (*resp)[i].resp = strdup(appdata->password);
00481                                                 break;
00482                                 }
00483 
00484                                 /* If there was an error during strdup(), 
00485                                  * clean up already allocated  structures and return PAM_CONV_ERR */
00486                                 if (!(*resp)[i].resp) {
00487                                         for(j = i; j >= 0 ; j--) {
00488                                                 if ((*resp)[j].resp)
00489                                                         free((*resp)[j].resp);
00490                                         }
00491                                         free(*resp);
00492                                         *resp = NULL;
00493                                         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
00494                                                         "Return PAM_CONV_ERROR due to strdup() failure");
00495                                         return PAM_CONV_ERR;
00496                                 }
00497                         }
00498                         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Return PAM_SUCCESS");
00499                         return PAM_SUCCESS;
00500                 }
00501         }
00502         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Return PAM_CONV_ERROR");
00503         return PAM_CONV_ERR;
00504 }
00505 #endif
00506 
00507 #define MGD_USERNAME_MAXLEN 80
00508 
00509 static int mgd_auth_su(midgard * mgd, const char *username,
00510                        const char *password, int su, int setuid, int trusted)
00511 {
00512         midgard_res *res;
00513         int i;
00514         const char *cipher, *passwd;
00515         int host_sitegroup, req_sitegroup, admingroup;
00516         char *sitegroup;
00517         int rootuser = 0;
00518         char delim = '+';
00519         int grant;
00520         char *reauth = NULL;    
00521 
00522         assert(mgd);
00523 
00524         if (mgd->msql->mysql == NULL)
00525                 return MGD_AUTH_NOT_CONNECTED;
00526 
00527         host_sitegroup = mgd->current_user->sitegroup;
00528         mgd_clear(mgd, MGD_CLEAR_BASE);
00529 
00530         if (setuid) {
00531                 mgd->current_user = &mgd->setuid_user;
00532                 mgd_clear(mgd, MGD_CLEAR_BASE);
00533                 mgd->current_user->sitegroup = host_sitegroup;
00534         }
00535 
00536         /* anonymous */
00537         if (!username || !*username) {
00538                 return MGD_AUTH_ANONYMOUS;
00539         }
00540 
00541         if (strlen(username) > MGD_USERNAME_MAXLEN)
00542                 return MGD_AUTH_INVALID_NAME;
00543 
00544 #define MIDGARD_LOGIN_SG_ROOT_SEP "*!$"
00545 
00546 #define MIDGARD_LOGIN_SG_ROOT_ROOT      '*'
00547 #define MIDGARD_LOGIN_SG_ROOT_ADMIN     '!'
00548 #define MIDGARD_LOGIN_SG_ROOT_USER      '$'
00549 #define MIDGARD_LOGIN_SG_ADMIN_USER     ';'
00550 #define MIDGARD_LOGIN_SG_USER_USER      '+'
00551 #define MIDGARD_LOGIN_SG_REAUTH         '='
00552 
00553         gchar *uname = g_strdup(username);
00554         
00555         sitegroup = strpbrk(uname, MIDGARD_LOGIN_SG_SEPARATOR);
00556 
00557         if (sitegroup) {
00558                 delim = *sitegroup;
00559                 *sitegroup = '\0';
00560                 sitegroup++;
00561                 rootuser = (strchr(MIDGARD_LOGIN_SG_ROOT_SEP, delim) != NULL);
00562 
00563                 if (rootuser && !*sitegroup) return MGD_AUTH_SG_NOTFOUND;
00564         
00565         } else if (host_sitegroup == 0) {
00566                 delim = MIDGARD_LOGIN_SG_ROOT_ROOT;
00567                 rootuser = 1;
00568         } else {
00569                 delim = MIDGARD_LOGIN_SG_USER_USER;
00570                 rootuser = 0;
00571         }
00572 
00573         reauth = strchr(uname, MIDGARD_LOGIN_SG_REAUTH);
00574         if (reauth) {
00575                 *reauth = '\0';
00576                 reauth++;
00577         }
00578 
00579         if ((su == 1) && (sitegroup || reauth)) {
00580                 g_free(uname);
00581                 return MGD_AUTH_REAUTH;
00582         }
00583 
00584         if (sitegroup && *sitegroup) {
00585                 res =
00586                    mgd_ungrouped_select(mgd, "id,admingroup", "sitegroup",
00587                                         "name=$q", NULL, sitegroup);
00588 
00589                 /* sitegroup must be found */
00590                 if (!res || !mgd_fetch(res)) {
00591                         if (res)
00592                                 mgd_release(res);
00593                         g_free(uname);
00594                         return MGD_AUTH_SG_NOTFOUND;
00595                 }
00596 
00597                 req_sitegroup = mgd_sql2id(res, 0);
00598                 admingroup = mgd_sql2id(res, 1);
00599                 mgd_release(res);
00600         }
00601         else {
00602                 req_sitegroup = host_sitegroup;
00603                 admingroup = 0;
00604 
00605                 res =
00606                    mgd_ungrouped_select(mgd, "admingroup", "sitegroup",
00607                                         "id=$d", NULL, host_sitegroup);
00608 
00609                 if (res) {
00610                         if (mgd_fetch(res))
00611                                 admingroup = mgd_sql2id(res, 0);
00612                         mgd_release(res);
00613                 }
00614         }
00615 
00616         if (rootuser) {
00617                 res = mgd_ungrouped_select(mgd, "person.id,person.password",
00618                                            "person,member",
00619                                            "person.username=$q AND person.sitegroup=0"
00620                                            " AND member.uid = person.id AND member.gid=0",
00621                                            NULL, uname);
00622         }
00623         else {
00624                 res = mgd_ungrouped_select(mgd, "id,password", "person",
00625                                            "username=$q AND sitegroup=$d",
00626                                            NULL, uname, req_sitegroup);
00627         }
00628 
00629         /* username not found */
00630         if (!res) {
00631                 g_free(uname);
00632                 return MGD_AUTH_NOTFOUND;
00633         }
00634 
00635         /* only one user may match */
00636         if (mgd_rows(res) != 1) {
00637       mgd_release(res);
00638                 return MGD_AUTH_DUPLICATE;
00639         }
00640 
00641         /* username not found */
00642         if (!res) {
00643                 g_free(uname);
00644                 return MGD_AUTH_NOTFOUND;
00645         }
00646 
00647         while (mgd->current_user->id == 0 && mgd_fetch(res)) {
00648                 
00649                 switch(mgd->auth_type) {
00650                 case MGD_AUTHTYPE_NORMAL:
00651                 
00652                         /* Ugly workaround for compiler warning message.
00653                          * 'fallback' label is available in function scope , so 
00654                          * no warning should be thrown. */
00655                         if( 0 > 1)
00656                                 goto fallback;
00657 
00658                         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
00659                                         "Authentication type: NORMAL");
00660                         fallback:
00661                         passwd = mgd_colvalue(res, 1);
00662 
00663                         if (passwd[0] == '*' && passwd[1] == '*') {
00664                                 passwd += 2;
00665                                 cipher = password;
00666                         }
00667                         else
00668 #ifdef WIN32
00669                                 g_warning("crypt not supported on Win32");
00670 #else
00671                         /* Conditional jump or move depends on uninitialised value(s) */        
00672                         cipher = (const char *)crypt(password, passwd);
00673 #endif
00674 
00675                         if (
00676                                 su || trusted ||
00677                                 !strcmp(cipher, passwd))
00678                                 mgd->current_user->id = mgd_sql2id(res, 0);
00679                         break;
00680 #ifdef HAVE_LIBPAM
00681                 case MGD_AUTHTYPE_PAM:
00682                         /* Authenticate against PAM source. We use predefined PAM service 'midgard' as auth source */
00683                         {
00684                                 _midgard_pam_appdata appdata = {
00685                                         (char*) uname, 
00686                                         (char*) password
00687                                 };
00688                                 
00689                                 struct pam_conv pconv = {
00690                                         _midgard_pam_conv,
00691                                         &appdata
00692                                 };
00693 
00694                                 pam_handle_t *phandle = NULL;
00695                                 
00696                                 int result = pam_start(mgd->pamfile, uname, &pconv, &phandle);
00697                                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
00698                                                 "PAM start result: %d", result);
00699                                 
00700                                 if (result == PAM_SUCCESS) 
00701                                         result = pam_authenticate(phandle, 0);
00702                                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
00703                                                 "PAM authenticate result: %d", result);
00704                                 if (su || (result == PAM_SUCCESS)) {
00705                                         mgd->current_user->id = mgd_sql2id(res, 0);
00706                                         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
00707                                                         "PAM auth was successful, new user id: %d", mgd->current_user->id);
00708                                 }
00709                                 result = pam_end(phandle, su ? PAM_SUCCESS : result);
00710                                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
00711                                                 "PAM end result: %d", result);
00712                                 if (mgd->current_user->id == 0) {
00713                                         /* Fallback to normal authentication */
00714                                         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
00715                                                         "PAM auth was unsuccessful, going to normal auth");
00716                                         goto fallback;
00717                           }
00718                         }
00719                 break;
00720 #endif
00721                 default:
00722                         g_error("Unsupported midgard authentication type %d", mgd->auth_type);
00723                 }
00724         }
00725 
00726         mgd_release(res);
00727 
00728         if (mgd->current_user->id == 0) {
00729                 g_free(uname);
00730                 return MGD_AUTH_INVALID_PWD;
00731         }
00732 
00733         /* Get group information */
00734         res = mgd_ungrouped_select(mgd, "gid", "member",
00735                                    "uid=$i AND sitegroup in (0, $i)",
00736                                    "gid DESC", mgd->current_user->id, req_sitegroup);
00737 
00738         if (res) { 
00739                 mgd->current_user->member_of = 
00740                         (int *) realloc(mgd->current_user->member_of,
00741                                         sizeof (int) * (mgd_rows(res) + 1));
00742                 i = 0;
00743                 while (mgd_fetch(res)) {
00744                         if ((mgd->current_user->member_of[i] = mgd_sql2id(res, 0)) != 0) {
00745                                 if (mgd->current_user->member_of[i] == admingroup)
00746                                         mgd->current_user->is_admin = 1;
00747                                 i++;
00748                         } else {
00749                                 mgd->current_user->is_admin = 1;
00750                                 mgd->current_user->is_root = 1;
00751                         }
00752                 }
00753 
00754                 mgd->current_user->member_of[i] = 0;
00755                 mgd_release(res);
00756         }
00757 
00758         grant = (rootuser && mgd->current_user->is_root);
00759         if (!grant && !rootuser && !mgd->current_user->is_root) {
00760                 if (host_sitegroup != 0 && req_sitegroup == host_sitegroup)
00761                         grant = 1;
00762                 if (host_sitegroup == 0 && req_sitegroup != 0)
00763                         grant = 1;
00764         }
00765 
00766         if (!grant) {
00767                 mgd_clear(mgd, MGD_CLEAR_BASE);
00768         }
00769         else {
00770                 /* Change current sitegroup */
00771                 mgd->current_user->sitegroup = req_sitegroup;
00772         }
00773 
00774         switch (delim) {
00775         case MIDGARD_LOGIN_SG_ROOT_USER:
00776         case MIDGARD_LOGIN_SG_ADMIN_USER:
00777                 mgd->current_user->is_admin = 0;
00778         case MIDGARD_LOGIN_SG_ROOT_ADMIN:
00779                 mgd->current_user->is_root = 0;
00780         }
00781 
00782         if (su == 1) {
00783                 if (!grant || mgd->current_user->sitegroup == 0
00784             || !mgd->current_user->is_admin || !reauth) {
00785                         mgd_clear(mgd, MGD_CLEAR_BASE);
00786                 }
00787                 else {
00788                         /* this depends on mgd->sitegroup already being set */
00789                         mgd_auth_su(mgd, reauth, "", 1, 0, FALSE);
00790                 }
00791         }
00792 
00793         /* schema and midgard_person check is done for repligard workaround
00794          * which doesn't use new configuration file */  
00795         if (mgd->schema != NULL ) {
00796                 if(g_type_from_name("midgard_person") > 0){                     
00797                         MgdObject *pobject = midgard_object_new_by_id(mgd,
00798                                         "midgard_person",
00799                                         (gchar *) mgd->current_user->id);               
00800                         mgd->person = pobject;                  
00801                 }
00802         }
00803         g_free(uname);
00804         return mgd->current_user->id;
00805 }
00806 
00807 int mgd_auth(midgard * mgd,
00808       const char *username, const char *password, int setuid)
00809 {
00810         return mgd_auth_su(mgd, username, password, 0, setuid, FALSE);
00811 }
00812 
00813 int mgd_auth_trusted(midgard *mgd, const char *username) {
00814         /* TODO: The mgd_auth_su() method needs to be refactored.    */
00815         /* Until that is done, we just add *another* special         */
00816         /* argument to overload the default behaviour. Not good. :-( */
00817         return mgd_auth_su(mgd, username, NULL, 0, FALSE, TRUE);
00818 }
00819 
00820 int mgd_auth_is_setuid(midgard * mgd)
00821 {
00822    return (mgd->current_user == &mgd->setuid_user);
00823 }
00824 
00825 MGD_API int mgd_auth_unsetuid(midgard * mgd, long int rsg)
00826 {
00827   if (mgd->current_user == &mgd->user_at_pagestart) return 0;
00828   mgd->current_user = &mgd->user_at_pagestart;
00829   mgd->current_user->sitegroup = rsg;
00830   g_log("midgard-lib", G_LOG_LEVEL_DEBUG, "Unset for SG: %d", (int) rsg);
00831   return 1;
00832 }
00833 
00834 void mgd_clear(midgard * mgd, int clearflags)
00835 {
00836         assert(mgd);
00837 
00838         /* clear user information */
00839         mgd->current_user->id = 0;
00840         mgd->current_user->is_admin = 0;
00841         mgd->current_user->is_root = 0;
00842    if (clearflags & MGD_CLEAR_SITEGROUP) { mgd->current_user->sitegroup = 0; }
00843         mgd->username = NULL;
00844 
00845         if (mgd->current_user->member_of != NULL)
00846            free(mgd->current_user->member_of);
00847         mgd->current_user->member_of = NULL;
00848 
00849         /* we automaticaly select first parser to avoid confusion */
00850         if (clearflags & MGD_CLEAR_PARSER) { mgd->parser = mgd_parser_list(); }
00851 
00852         /* clear resources reserved */
00853         while (mgd->res) mgd_release(mgd->res);
00854 
00855         mgd_clear_pool(mgd->pool);
00856 }
00857 
00858 int mgd_errno(midgard * mgd)
00859 {
00860         assert(mgd);
00861         return mysql_errno(mgd->msql->mysql);
00862 }
00863 
00864 const char *mgd_error(midgard * mgd)
00865 {
00866         assert(mgd);
00867         return mysql_error(mgd->msql->mysql);
00868 }
00869 
00870 int mgd_select_parser(midgard * mgd, const char *name)
00871 {
00872         return mgd_parser_activate(mgd, name);
00873 }
00874 
00875 const char *mgd_get_parser_name(midgard * mgd)
00876 {
00877         if (mgd && mgd->parser)
00878                 return mgd->parser->name;
00879         return NULL;
00880 }
00881 
00882 const char *mgd_get_encoding(midgard * mgd)
00883 {
00884         if (mgd && mgd->parser)
00885                 return mgd->parser->encoding;
00886         return NULL;
00887 }
00888 
00889 int mgd_mail_need_qp(midgard * mgd)
00890 {
00891         if (mgd && mgd->parser)
00892                 return mgd->parser->need_qp;
00893         return 0;
00894 }
00895 
00896 void mgd_set_sitegroup(midgard * mgd, int sitegroup)
00897 {
00898         assert(mgd);
00899         mgd->current_user->sitegroup = sitegroup;
00900 }
00901 int mgd_sitegroup(midgard * mgd)
00902 {
00903         assert(mgd);
00904         return mgd->current_user->sitegroup;
00905 }
00906 
00907 int mgd_user(midgard * mgd)
00908 {
00909         assert(mgd);
00910         return mgd->current_user->id;
00911 }
00912 
00913 int mgd_isauser(midgard * mgd)
00914 {
00915         assert(mgd);
00916         return (mgd->current_user->id != 0);
00917 }
00918 
00919 int mgd_isadmin(midgard * mgd)
00920 {
00921         assert(mgd);
00922         return mgd->current_user->is_admin;
00923 }
00924 
00925 int mgd_isroot(midgard * mgd)
00926 {
00927         assert(mgd);
00928         return mgd->current_user->is_root;
00929 }
00930 
00931 void mgd_force_root(midgard * mgd)
00932 {
00933         assert(mgd);
00934         mgd->current_user->is_root = 1;
00935         mgd->current_user->is_admin = 1;
00936         mgd->current_user->id = 0;
00937 }
00938 
00939 void mgd_force_admin(midgard * mgd)
00940 {
00941         assert(mgd);
00942         mgd->current_user->is_admin = 1;
00943         mgd->current_user->id = 0;
00944 }
00945 
00946 
00947 #if HAVE_MIDGARD_MULTILANG
00948 int mgd_lang(midgard * mgd)
00949 {
00950         assert(mgd);
00951         return mgd->lang;
00952 }
00953 
00954 void mgd_set_default_lang(midgard *mgd, int lang)
00955 {
00956         g_assert(mgd);
00957         mgd->default_lang = lang;
00958         mgd->lang = lang;
00959 }
00960 
00961 int mgd_get_default_lang(midgard *mgd)
00962 {
00963         g_assert(mgd);
00964         return mgd->default_lang;
00965 }
00966 
00967 int mgd_parameters_defaultlang(midgard * mgd)
00968 {
00969         assert(mgd);
00970         return mgd->p_lang;
00971 }
00972 int mgd_attachments_defaultlang(midgard * mgd)
00973 {
00974         assert(mgd);
00975         return mgd->a_lang;
00976 }
00977 
00978 #endif /* HAVE_MIDGARD_MULTILANG */
00979 int mgd_isuser(midgard * mgd, int user)
00980 {
00981         assert(mgd);
00982         return (mgd->current_user->id == user);
00983 }
00984 
00985 int mgd_ismember(midgard * mgd, int group)
00986 {
00987         int i;
00988    int *member_of;
00989 
00990         assert(mgd);
00991 
00992    member_of = mgd->current_user->member_of;
00993 
00994         if (member_of == NULL) return 0;
00995 
00996         for (i = 0; member_of[i]; i++) if (member_of[i] == group) return 1;
00997 
00998         return 0;
00999 }
01000 
01001 int *mgd_groups(midgard * mgd)
01002 {
01003         return mgd->current_user->member_of;
01004 }
01005 
01006 midgard_res *mgd_query(midgard * mgd, const char *query, ...)
01007 {
01008         midgard_res *res;
01009         va_list args;
01010         va_start(args, query);
01011         res = mgd_vquery(mgd, query, args);
01012         va_end(args);
01013         return res;
01014 }
01015 
01016 midgard_res *mgd_vquery(midgard * mgd, const char *query, va_list args)
01017 {
01018         midgard_res *res;
01019         midgard_pool *pool;
01020         MYSQL_RES *mres;
01021         int rv;
01022         char *fquery;
01023         assert(mgd);
01024         assert(query);
01025         
01026         if (mgd->msql->mysql == NULL)
01027                 return NULL;
01028 
01029         /* format and send query */
01030         pool = mgd_alloc_pool();
01031         if (!pool)
01032                 return NULL;
01033         fquery = mgd_vformat(mgd, pool, query, args);
01034         if (!fquery) {
01035                 mgd_free_pool(pool);
01036                 return NULL;
01037         }
01038 
01039         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "query=%s", fquery);
01040 
01041         rv = mysql_query(mgd->msql->mysql, fquery);
01042         
01043         if (rv != 0) {
01044                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
01045                                 "QUERY: \n"
01046                                 "%s \n"
01047                                 "FAILED: \n"
01048                                 "%s", 
01049                                 fquery, mysql_error(mgd->msql->mysql));
01050                 return NULL;
01051         }
01052 
01053         g_free(fquery);
01054         mgd_free_pool(pool);
01055         
01056         /* get results */
01057         mres = mysql_store_result(mgd->msql->mysql);
01058         if (!mres)
01059                 return NULL;
01060         if (mysql_num_rows(mres) == 0) {
01061                 mysql_free_result(mres);
01062                 return NULL;
01063         }
01064         /* create result handle */
01065         res = g_new(midgard_res, 1);
01066         if (!res) {
01067                 mysql_free_result(mres);
01068                 return NULL;
01069         }
01070         res->pool = mgd_alloc_pool();
01071         if (!res->pool) {
01072                 free(res);
01073                 mysql_free_result(mres);
01074                 return NULL;
01075         }
01076         res->mgd = mgd;
01077         res->msql = g_new(midgard_mysql, 1);
01078         res->msql->res = mres;
01079         res->msql->row = NULL;
01080         res->rown = 0;
01081         res->rows = mysql_num_rows(mres);       
01082         res->cols = mysql_num_fields(mres);
01083         res->msql->fields = mysql_fetch_fields(mres);
01084 
01085         /* insert into result chain */
01086         if (mgd->res)
01087                 mgd->res->prev = res;
01088         res->prev = NULL;
01089         res->next = mgd->res;
01090         mgd->res = res;
01091 
01092         return res;
01093 }
01094 
01095 midgard_res *mgd_ungrouped_select(midgard * mgd,
01096                                   const char *fields, const char *from,
01097                                   const char *where, const char *sort, ...)
01098 {
01099         midgard_res *res;
01100         va_list args;
01101         va_start(args, sort);
01102         res = mgd_ungrouped_vselect(mgd, fields, from, where, sort, args);
01103         va_end(args);
01104         return res;
01105 }
01106 
01107 midgard_res *mgd_sitegroup_select(midgard * mgd,
01108                                   const char *fields, const char *from,
01109                                   const char *where, const char *sort, ...)
01110 {
01111         midgard_res *res;
01112         va_list args;
01113         va_start(args, sort);
01114         res = mgd_sitegroup_vselect(mgd, fields, from, where, sort, args);
01115         va_end(args);
01116         return res;
01117 }
01118 
01119 midgard_res *mgd_ungrouped_vselect(midgard * mgd,
01120                                    const char *fields, const char *from,
01121                                    const char *where, const char *sort,
01122                                    va_list args)
01123 {
01124         midgard_res *res;
01125         assert(fields && from);
01126         GString *query = g_string_new("SELECT ");
01127         
01128         /* format query */
01129         if (where)
01130                 if (sort)
01131                         g_string_append_printf(query,
01132                                         "%s FROM %s WHERE %s ORDER BY %s",
01133                                         fields, from, where, sort);
01134                 else
01135                         g_string_append_printf(query,
01136                                         "%s FROM %s WHERE %s",
01137                                         fields, from, where);
01138                 
01139         else if (sort)
01140                 g_string_append_printf(query,
01141                                 "%s FROM %s ORDER BY %s",
01142                                 fields, from, sort);
01143         else
01144                 g_string_append_printf(query,
01145                                 "%s FROM %s ",
01146                                 fields, from);
01147                 
01148 
01149         gchar *tmpstr = g_string_free(query, FALSE);
01150         /* execute query */
01151         res = mgd_vquery(mgd, tmpstr, args);
01152         g_free(tmpstr);
01153 
01154         return res;
01155 }
01156 
01157 /* EmHe: add support for named tables at some point */
01158 char *mgd_sitegroup_clause(midgard * mgd, midgard_pool * pool,
01159                            const char *tables)
01160 {
01161         char *table , *tmptable;
01162         const char *sgclause;
01163 
01164         if (mgd->current_user->is_root) {
01165                 return g_strdup("");
01166         }
01167 
01168         
01169         tmptable = g_strdup(tables);
01170         table = strtok((gchar *)tmptable, " ,");        
01171 
01172         while (table && !*table)
01173                 table = strtok(NULL, " ,");
01174 
01175         if (!table) {   
01176                 return g_strdup("AND 0=1");
01177         }                       /* EmHe: this is an error */
01178 
01179 #if HAVE_MIDGARD_MULTILANG
01180         if (!strstr(table, "_i")) {
01181 #endif /* HAVE_MIDGARD_MULTILANG */
01182         sgclause = mgd_format(mgd, pool, "AND ($s.sitegroup in (0,$d)",
01183                               table, mgd->current_user->sitegroup);
01184 #if HAVE_MIDGARD_MULTILANG
01185         } else {
01186           sgclause = mgd_strdup(pool, "1=1");
01187         }
01188 #endif /* HAVE_MIDGARD_MULTILANG */
01189 
01190         if (!sgclause) {
01191                 return g_strdup("AND 0=2");
01192         }                       /* EmHe: this is an error */
01193 
01194         for (table = strtok(NULL, " ,"); table; table = strtok(NULL, " ,")) {
01195                 if (!*table)
01196                         continue;
01197 
01198 #if HAVE_MIDGARD_MULTILANG
01199                 if (!strstr(table, "_i")) {
01200 #endif /* HAVE_MIDGARD_MULTILANG */
01201                 sgclause =
01202                    mgd_format(mgd, pool, "$s AND $s.sitegroup in (0, $d)",
01203                               sgclause, table, mgd->current_user->sitegroup);
01204 #if HAVE_MIDGARD_MULTILANG
01205                 }
01206 #endif /* HAVE_MIDGARD_MULTILANG */
01207 
01208                 if (!sgclause) {
01209                         return g_strdup("AND 0=3");
01210                 }               /* EmHe: this is an error */
01211         }
01212         
01213         g_free(table);
01214         return mgd_format(mgd, pool, "$s)", sgclause);
01215 }
01216 
01217 midgard_res *mgd_sitegroup_vselect(midgard * mgd,
01218                                    const char *fields, const char *from,
01219                                    const char *where, const char *sort,
01220                                    va_list args)
01221 {
01222         midgard_res *res;
01223         const char *query;
01224         midgard_pool *pool;
01225         const char *sgclause;
01226         assert(fields && from);
01227         pool = mgd_alloc_pool();
01228         if (!pool)
01229                 return NULL;
01230 
01231         sgclause = mgd_sitegroup_clause(mgd, pool, from);
01232 
01233         if (sort)
01234                 query = mgd_format(mgd, pool,
01235                                    "SELECT $s FROM $s WHERE ($s) $s"
01236                                    " ORDER BY $s",
01237                                    fields, from, where ? where : "1=1",
01238                                    sgclause, sort);
01239         else
01240                 query = mgd_format(mgd, pool,
01241                                    "SELECT $s FROM $s WHERE ($s) $s",
01242                                    fields, from, where ? where : "1=1",
01243                                    sgclause);
01244 
01245         g_free((gchar *)sgclause);
01246         
01247         if (!query) {
01248                 mgd_free_pool(pool);
01249                 return NULL;
01250         }
01251 
01252         /* execute query */
01253         res = mgd_vquery(mgd, query, args);
01254         g_free((gchar *)query);
01255 
01256         mgd_free_pool(pool);
01257         return res;
01258 }
01259 
01260 midgard_res *mgd_ungrouped_record(midgard * mgd,
01261                                   const char *fields, const char *table, int id)
01262 {
01263         midgard_res *res;
01264         const char *query;
01265         midgard_pool *pool;
01266         pool = mgd_alloc_pool();
01267         if (!pool)
01268                 return NULL;
01269 
01270         /* format query */
01271 #if ! HAVE_MIDGARD_MULTILANG
01272         query = mgd_format(mgd, pool, "SELECT $s FROM $s WHERE id=$i",
01273                            fields, table, id);
01274 #else /* HAVE_MIDGARD_MULTILANG */
01275         query = mgd_format(mgd, pool, "SELECT $s FROM $s WHERE $s.id=$i",
01276                            fields, table, table, id);
01277 #endif /* HAVE_MIDGARD_MULTILANG */
01278         if (!query) {
01279                 mgd_free_pool(pool);
01280                 return NULL;
01281         }
01282 
01283         /* execute query */
01284         res = mgd_query(mgd, query);
01285         g_free((gchar *)query);
01286 
01287         mgd_free_pool(pool);
01288         return res;
01289 }
01290 
01291 midgard_res *mgd_sitegroup_record(midgard * mgd,
01292                                   const char *fields, const char *table, int id)
01293 {
01294         midgard_res *res;
01295         const char *query;
01296         midgard_pool *pool;
01297 
01298         pool = mgd_alloc_pool();
01299         if (!pool)
01300                 return NULL;
01301 
01302         /* format query */
01303         query = mgd_format(mgd, pool,
01304                            "SELECT $s,sitegroup FROM $s WHERE id=$i AND (sitegroup in (0, $d) OR $d<>0)",
01305                            fields, table, id,
01306             mgd->current_user->sitegroup,
01307             mgd->current_user->