/*
** Copyright (c) 2002 D. Richard Hipp
**
** 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 library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Routines for handling user account
*/
#define _XOPEN_SOURCE
#include <unistd.h>
#include "config.h"
#include "user.h"

/*
** WEBPAGE: /userlist
*/
void user_list(void){
  char **azResult;
  int i;

  login_check_credentials();
  if( !g.okWrite && g.isAnon ){
    login_needed();
    return;
  }
  common_standard_menu("userlist", 0);
  common_add_help_item("CvstracAdminUsers");
  common_add_action_item("useredit", "Add User");
  common_header("User List");
  cgi_printf("<table cellspacing=0 cellpadding=0 border=0>\n"
         "<tr>\n"
         "  <th align=\"right\"><nobr>User ID</nobr></th>\n"
         "  <th>&nbsp;&nbsp;&nbsp;Permissions&nbsp;&nbsp;&nbsp;</th>\n"
         "  <th><nobr>In Real Life</nobr></th>\n"
         "</tr>\n");
  azResult = db_query(
    "SELECT id, name, email, capabilities FROM user ORDER BY id");
  for(i=0; azResult[i]; i+= 4){
    cgi_printf("<tr>\n"
           "<td align=\"right\">\n");
    if( g.okAdmin ){
      cgi_printf("<a href=\"useredit?id=%t\">\n",azResult[i]);
    }
    cgi_printf("<nobr>%h</nobr>\n",azResult[i]);
    if( g.okAdmin ){
      cgi_printf("</a>\n");
    }
    cgi_printf("</td>\n"
           "<td align=\"center\">%s</td>\n",azResult[i+3]);
    if( azResult[i+2] && azResult[i+2][0] ){
      char *zE = azResult[i+2];
      cgi_printf("<td align=\"left\"><nobr>%h\n"
             "   (<a href=\"mailto:%h\">%h</a>)</nobr></td>\n",azResult[i+1],zE,zE);
    } else {
      cgi_printf("<td align=\"left\"><nobr>%h</nobr></td>\n",azResult[i+1]);
    }
    cgi_printf("</tr>\n");
  }
  cgi_printf("</table>\n"
         "<p><hr>\n"
         "<b>Notes:</b>\n"
         "<ol>\n"
         "<li><p>The permission flags are as follows:</p>\n"
         "<table>\n"
         "<tr><td>a</td><td width=\"10\"></td>\n"
         "    <td>Admin: Create or delete users and ticket report formats</td></tr>\n"
         "<tr><td>d</td><td></td>\n"
         "    <td>Delete: Erase anonymous wiki, tickets, and attachments</td></tr>\n"
         "<tr><td>i</td><td></td>\n"
         "    <td>Check-in: Add new code to the %h repository</td></tr>\n"
         "<tr><td>j</td><td></td><td>Read-Wiki: View wiki pages</td></tr>\n"
         "<tr><td>k</td><td></td><td>Wiki: Create or modify wiki pages</td></tr>\n"
         "<tr><td>n</td><td></td><td>New: Create new tickets</td></tr>\n"
         "<tr><td>o</td><td></td>\n"
         "    <td>Check-out: Read code out of the %h repository</td></tr>\n"
         "<tr><td>p</td><td></td><td>Password: Change password</td></tr>\n"
         "<tr><td>q</td><td></td><td>Query: Create or edit report formats</td></tr>\n"
         "<tr><td>r</td><td></td><td>Read: View tickets and change histories</td></tr>\n"
         "<tr><td>s</td><td></td><td>Setup: Change CVSTrac options</td></tr>\n"
         "<tr><td>w</td><td></td><td>Write: Edit tickets</td></tr>\n"
         "</table>\n"
         "</p></li>\n"
         "\n"
         "<li><p>\n"
         "If a user named \"<b>anonymous</b>\" exists, then anyone can access\n"
         "the server without having to log in.  The permissions on the\n"
         "anonymous user determine the access rights for anyone who is not\n"
         "logged in.\n"
         "</p></li>\n"
         "\n",g.scm.zName,g.scm.zName);
  if( !strcmp(g.scm.zSCM,"cvs") ){
    cgi_printf("<li><p>\n"
           "You must be using CVS version 1.11 or later in order to give users\n"
           "read-only access to the repository.\n"
           "With earlier versions of CVS, all users with check-out\n"
           "privileges also automatically get check-in privileges.\n"
           "</p></li>\n"
           "\n"
           "<li><p>\n"
           "Changing a users ID or password modifies the <b>CVSROOT/passwd</b>,\n"
           "<b>CVSROOT/readers</b>, and <b>CVSROOT/writers</b> files in the CVS\n"
           "repository, if those files have write permission turned on.  Users\n"
           "IDs in <b>CVSROOT/passwd</b> that are unknown to CVSTrac are preserved.\n");
    if( g.okSetup ){
      cgi_printf("Use the \"Import CVS Users\" button on the \n"
             "<a href=\"setup_user\">user setup</a> page\n"
             "to import CVS users into CVSTrac.\n");
    }
    cgi_printf("</p></li>\n");
  }
  cgi_printf("</ol>\n");
  common_footer();
}

/*
** WEBPAGE: /useredit
*/
void user_edit(void){
  char **azResult;
  const char *zId, *zName, *zEMail, *zCap;
  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
  char *oak, *oad, *oaq;
  int doWrite;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */
  login_check_credentials();
  if( !g.okAdmin ){ login_needed(); return; }

  /* Check to see if an ADMIN user is trying to edit a SETUP account.
  ** Don't allow that.
  */
  zId = P("id");
  if( zId && !g.okSetup ){
    char *zOldCaps;
    zOldCaps = db_short_query(
       "SELECT capabilities FROM user WHERE id='%q'",zId);
    higherUser = zOldCaps && strchr(zOldCaps,'s');
  }

  if( !higherUser ){
    if( P("delete") ){
      common_add_action_item("userlist", "Cancel");
      common_header("Are You Sure?");
      cgi_printf("<form action=\"useredit\" method=\"POST\">\n"
             "<p>You are about to delete the user <strong>%h</strong> from\n"
             "the database.  This is an irreversible operation.</p>\n"
             "\n"
             "<input type=\"hidden\" name=\"id\" value=\"%t\">\n"
             "<input type=\"hidden\" name=\"nm\" value=\"\">\n"
             "<input type=\"hidden\" name=\"em\" value=\"\">\n"
             "<input type=\"hidden\" name=\"pw\" value=\"\">\n"
             "<input type=\"submit\" name=\"delete2\" value=\"Delete The User\">\n"
             "<input type=\"submit\" name=\"can\" value=\"Cancel\">\n"
             "</form>\n",zId,zId);
      common_footer();
      return;
    }else if( P("can") ){
      cgi_redirect("userlist");
      return;
    }
  }

  /* If we have all the necessary information, write the new or
  ** modified user record.  After writing the user record, redirect
  ** to the page that displays a list of users.
  */
  doWrite = zId && zId[0] && cgi_all("nm","em","pw") && !higherUser;
  if( doWrite ){
    const char *zOldPw;
    char zCap[20];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=0;
    int ai = P("ai")!=0;
    int aj = P("aj")!=0;
    int ak = P("ak")!=0;
    int an = P("an")!=0;
    int ao = P("ao")!=0;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
    int ar = P("ar")!=0;
    int as = g.okSetup && P("as")!=0;
    int aw = P("aw")!=0;
    if( as ) aa = 1;
    if( aa ) ai = aw = ap = 1;
    if( aw ) an = ar = 1;
    if( ai ) ao = 1;
    if( ak ) aj = 1;
    if( aa ){ zCap[i++] = 'a'; }
    if( ad ){ zCap[i++] = 'd'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( an ){ zCap[i++] = 'n'; }
    if( ao ){ zCap[i++] = 'o'; }
    if( ap ){ zCap[i++] = 'p'; }
    if( aq ){ zCap[i++] = 'q'; }
    if( ar ){ zCap[i++] = 'r'; }
    if( as ){ zCap[i++] = 's'; }
    if( aw ){ zCap[i++] = 'w'; }

    zCap[i] = 0;
    zOldPw = db_short_query("SELECT passwd FROM user WHERE id='%q'", zId);
    db_execute("DELETE FROM user WHERE id='%q'", zId);
    if( !P("delete2") ){
      const char *zPw = P("pw");
      char zBuf[3];
      if( zOldPw==0 ){
        char zSeed[100];
        const char *z;
        bprintf(zSeed,sizeof(zSeed),"%d%.20s",getpid(),zId);
        z = crypt(zSeed, "aa");
        zBuf[0] = z[2];
        zBuf[1] = z[3];
        zBuf[2] = 0;
        zOldPw = zBuf;
      }
      db_execute(
         "INSERT INTO user(id,name,email,passwd,capabilities) "
         "VALUES('%q','%q','%q','%q','%s')",
         zId, P("nm"), P("em"), zPw[0] ? crypt(zPw, zOldPw) : zOldPw, zCap
      );
    }else{
      /* User was default assigned user id. Remove the default. */
      db_execute( "DELETE FROM config WHERE "
          "  name='assignto' AND value='%q'", zId);
    }

    /*
    ** The SCM subsystem may be able to replicate the user db somewhere...
    */
    if( g.scm.pxUserWrite ) g.scm.pxUserWrite(P("delete2")!=0 ? zId : 0);

    cgi_redirect("userlist");
    return;
  }

  /* Load the existing information about the user, if any
  */
  zName = "";
  zEMail = "";
  zCap = "";
  oaa = oad = oai = oaj = oak = oan = oao = oap = oaq = oar = oas = oaw = "";
  if( zId ){
    azResult = db_query(
      "SELECT name, email, capabilities FROM user WHERE id='%q'", zId
    );
    if( azResult && azResult[0] ){
      zName = azResult[0];
      zEMail = azResult[1];
      zCap = azResult[2];
      if( strchr(zCap, 'a') ) oaa = " checked";
      if( strchr(zCap, 'd') ) oad = " checked";
      if( strchr(zCap, 'i') ) oai = " checked";
      if( strchr(zCap, 'j') ) oaj = " checked";
      if( strchr(zCap, 'k') ) oak = " checked";
      if( strchr(zCap, 'n') ) oan = " checked";
      if( strchr(zCap, 'o') ) oao = " checked";
      if( strchr(zCap, 'p') ) oap = " checked";
      if( strchr(zCap, 'q') ) oaq = " checked";
      if( strchr(zCap, 'r') ) oar = " checked";
      if( strchr(zCap, 's') ) oas = " checked";
      if( strchr(zCap, 'w') ) oaw = " checked";
    }else{
      zId = 0;
    }
  }

  /* Begin generating the page
  */
  common_standard_menu(0,0);
  common_add_help_item("CvstracAdminUsers");
  common_add_action_item("userlist", "Cancel");
  common_add_action_item(mprintf("useredit?delete=1&id=%t",zId), "Delete");
  if( zId ){
    common_header("Edit User %s", zId);
  }else{
    common_header("Add New User");
  }
  cgi_printf("<form action=\"%s\" method=\"POST\">\n"
         "<table align=\"left\" hspace=10 vspace=10>\n"
         "<tr>\n"
         "  <td align=\"right\"><nobr>User ID:</nobr></td>\n",g.zPath);
  if( zId ){
    cgi_printf("  <td>%h <input type=\"hidden\" name=\"id\" value=\"%h\"></td>\n",zId,zId);
  }else{
    cgi_printf("  <td><input type=\"text\" name=\"id\" size=10></td>\n");
  }
  cgi_printf("</tr>\n"
         "<tr>\n"
         "  <td align=\"right\"><nobr>Full Name:</nobr></td>\n"
         "  <td><input type=\"text\" name=\"nm\" value=\"%h\"></td>\n"
         "</tr>\n"
         "<tr>\n"
         "  <td align=\"right\"><nobr>E-Mail:</nobr></td>\n"
         "  <td><input type=\"text\" name=\"em\" value=\"%h\"></td>\n"
         "</tr>\n"
         "<tr>\n"
         "  <td align=\"right\" valign=\"top\">Capabilities:</td>\n"
         "  <td>\n"
         "    <input type=\"checkbox\" name=\"aa\"%s>Admin</input><br>\n"
         "    <input type=\"checkbox\" name=\"ad\"%s>Delete</input><br>\n"
         "    <input type=\"checkbox\" name=\"ai\"%s>Check-In</input><br>\n"
         "    <input type=\"checkbox\" name=\"aj\"%s>Read Wiki</input><br>\n"
         "    <input type=\"checkbox\" name=\"ak\"%s>Write Wiki</input><br>\n"
         "    <input type=\"checkbox\" name=\"an\"%s>New Tkt</input><br>\n"
         "    <input type=\"checkbox\" name=\"ao\"%s>Check-Out</input><br>\n"
         "    <input type=\"checkbox\" name=\"ap\"%s>Password</input><br>\n"
         "    <input type=\"checkbox\" name=\"aq\"%s>Query</input><br>\n"
         "    <input type=\"checkbox\" name=\"ar\"%s>Read</input><br>\n",zName,zEMail,oaa,oad,oai,oaj,oak,oan,oao,oap,oaq,oar);
  if( g.okSetup ){
    cgi_printf("    <input type=\"checkbox\" name=\"as\"%s>Setup</input><br>\n",oas);
  }
  cgi_printf("    <input type=\"checkbox\" name=\"aw\"%s>Write</input>\n"
         "  </td>\n"
         "</tr>\n"
         "<tr>\n"
         "  <td align=\"right\">Password:</td>\n"
         "  <td><input type=\"password\" name=\"pw\" value=\"\"></td>\n"
         "</tr>\n",oaw);
  if( !higherUser ){
    cgi_printf("<tr>\n"
           "  <td>&nbsp</td>\n"
           "  <td><input type=\"submit\" name=\"submit\" value=\"Apply Changes\">\n"
           "      &nbsp;&nbsp;&nbsp;\n"
           "      <input type=\"submit\" name=\"delete\" value=\"Delete User\"></td>\n"
           "</tr>\n");
  }
  cgi_printf("</table>\n"
         "<p><b>Notes:</b></p>\n"
         "<ol>\n");
  if( higherUser ){
    cgi_printf("<li><p>\n"
           "User %h has Setup privileges and you only have Admin privileges\n"
           "so you are not permitted to make changes to %h.\n"
           "</p></li>\n"
           "\n",zId,zId);
  }
  if( g.scm.pxUserWrite!=0
        && !strcmp("yes",db_config("write_cvs_passwd","yes")) ){
    cgi_printf("<li><p>\n"
           "If the <b>Check-out</b> capability is specified then\n"
           "the password entered here will be used to regenerate the\n"
           "<b>CVSROOT/passwd</b> file and will thus become the CVS password\n"
           "as well as the password for this server.\n"
           "</p></li>\n"
           "\n"
           "<li><p>\n"
           "The <b>Check-in</b> capability means that the user ID will be written\n"
           "into the <b>CVSROOT/writers</b> file and thus allow write access to\n"
           "the CVS repository.\n"
           "</p></li>\n"
           "\n");
  }else{
    cgi_printf("<li><p>\n"
           "If the <b>Check-out</b> capability is specified then the user will be able\n"
           "to browse the %s repository.\n"
           "</p></li>\n"
           "\n"
           "<li><p>\n"
           "The <b>Check-in</b> capability gives the user the ability to edit check-in\n"
           "messages.\n"
           "</p></li>\n"
           "\n",g.scm.zName);
  }
  cgi_printf("<li><p>\n"
         "The <b>Read</b> and <b>Write</b> privileges give the user the ability\n"
         "to read and write tickets.  The <b>New Tkt</b> capability means that\n"
         "the user is able to create new tickets.\n"
         "</p></li>\n"
         "\n"
         "<li><p>\n"
         "The <b>Delete</b> privilege give the user the ability to erase\n"
         "wiki, tickets, and atttachments that have been added by anonymous\n"
         "users.  This capability is intended for deletion of spam.\n"
         "</p></li>\n"
         "\n"
         "<li><p>\n"
         "The <b>Query</b> privilege allows the user to create or edit\n"
         "report formats by specifying appropriate SQL.  Users can run \n"
         "existing reports without the Query privilege.\n"
         "</p></li>\n"
         "\n"
         "<li><p>\n"
         "An <b>Admin</b> user can add other users, create new ticket report\n"
         "formats, and change system defaults.  But only the <b>Setup</b> user\n"
         "is able to change the %h repository to\n"
         "which this program is linked.\n"
         "</p></li>\n"
         "\n",g.scm.zName);
  if( zId==0 || strcmp(zId,"anonymous")==0 ){
    cgi_printf("<li><p>\n"
           "No login is required for user \"<b>anonymous</b>\".  The capabilities\n"
           "of this user are available to anyone without supplying a username or\n"
           "password.  To disable anonymous access, make sure there is no user\n"
           "with an ID of <b>anonymous</b>.\n"
           "</p></li>\n"
           "\n"
           "<li><p>\n"
           "The password for the \"<b>anonymous</b>\" user is used for anonymous\n"
           "%h access.  The recommended value for the anonymous password\n"
           "is \"anonymous\".\n"
           "</p></li>\n",g.scm.zName);
  }
  cgi_printf("</form>\n");
  common_footer();
}

/*
** Remove the newline from the end of a string.
*/
void remove_newline(char *z){
  while( *z && *z!='\n' && *z!='\r' ){ z++; }
  if( *z ){ *z = 0; }
}


syntax highlighted by Code2HTML, v. 0.9.1