docs: public int login(Task task, String strUserName, String strPassword, String strDomain)
      {
          boolean bCreateServer = false;
          if ((this.getProperty(DBParams.REMOTE_HOST) != null) && (this.getProperty(DBParams.REMOTE_HOST).length() > 0))
              bCreateServer = true;
          org.jbundle.thin.base.remote.RemoteTask remoteTask = (org.jbundle.thin.base.remote.RemoteTask)this.getRemoteTask(null, strUserName, bCreateServer);
          if (remoteTask != null)
              return super.login(task, strUserName, strPassword, strDomain); // Remote client - have server log me in
          else
          {   // If I'm not remote, I need to read the user info.
              Map<String,Object> mapDomainProperties = this.setDomainProperties(task, strDomain);
              this.setProperty(Params.USER_ID, null);
              this.setProperty(Params.USER_NAME, null);
              if (Utility.isNumeric(strUserName))
                  this.setProperty(Params.USER_ID, strUserName);
              else
                  this.setProperty(Params.USER_NAME, strUserName);
              if ((this.getProperty(Params.USER_ID) == null) || (this.getProperty(Params.USER_ID).length() == 0))
                  if ((this.getProperty(Params.USER_NAME) == null) || (this.getProperty(Params.USER_NAME).length() == 0))
              {   // If user is not specified (either for no user, or logout), use the anonymous user
                  Record recUserControl = null;
                  if (m_systemRecordOwner != null)
                  {   // Always
                      recUserControl = (Record)m_systemRecordOwner.getRecord(UserControlModel.USER_CONTROL_FILE);
                      if (recUserControl == null)
                          recUserControl = Record.makeRecordFromClassName(UserControlModel.THICK_CLASS, m_systemRecordOwner);
                      String strUserID = recUserControl.getField(UserControlModel.ANON_USER_INFO_ID).getString();
                      if ((strUserID == null) || (strUserID.length() == 0))
                          strUserID = DBConstants.ANON_USER_ID;
                      this.setProperty(Params.USER_ID, strUserID);
                  }
              }
              if (this.readUserInfo(false, true) == true)
              {
                  UserInfoModel recUserInfo = this.getUserInfo();
                  if ((strPassword != null) && (strPassword.length() > 0))
                  {
                      while (strPassword.endsWith("%3D")) {
                          strPassword = strPassword.substring(0, strPassword.length() - 3) + "="; // Often converted when passing URLs
                      }
                      if (!strPassword.equals(recUserInfo.getField(UserInfoModel.PASSWORD).toString()))
                      {
                          return task.setLastError(this.getString("User name and password do not match"));
                      }
                      // Create and save the remote authorization token.
                      String strToken = Long.toString((long)(Math.random() * Long.MAX_VALUE));
                      strToken = Base64.encode(strToken);
                      this.setProperty(DBParams.AUTH_TOKEN, strToken);
                      // todo(don) HACK - For now, authentication is the SHA password! See ServletTask/777
                      this.setProperty(DBParams.AUTH_TOKEN, strPassword);    // NO NO NO                    
                  }
  
                  Record recUserGroup = ((ReferenceField)recUserInfo.getField(UserInfoModel.USER_GROUP_ID)).getReference();
                  String strSecurityMap = null;
                  Map<String,Object> mapUserProperties = ((PropertiesField)recUserInfo.getField(UserInfoModel.PROPERTIES)).getProperties();
                  Map<String,Object> mapGroupProperties = null;
                  if (recUserGroup != null)
                  {
                      strSecurityMap = recUserGroup.getField(UserGroupModel.ACCESS_MAP).toString();
                      mapGroupProperties = ((PropertiesField)recUserGroup.getField(UserGroupModel.PROPERTIES)).getProperties();
                  }
                  if (strSecurityMap == null)
                      strSecurityMap = DBConstants.BLANK;     // The signals the the user is signed on
                  this.setProperty(Params.SECURITY_MAP, strSecurityMap);
                  this.setProperty(Params.SECURITY_LEVEL, ((strPassword == null) || (strPassword.length() == 0)) ? Integer.toString(Constants.LOGIN_USER) : Integer.toString(Constants.LOGIN_AUTHENTICATED));
                  if ((DBConstants.ANON_USER_ID.equals(recUserInfo.getField(UserInfoModel.ID).toString()))
                          || (recUserInfo == null))
                      this.setProperty(Params.SECURITY_LEVEL, Integer.toString(Constants.LOGIN_USER));   // Special case - If user is anonymous, level is always anonymous
                  Map<String,Object> properties = new Hashtable<String,Object>();
                  if (mapDomainProperties != null)
                      properties.putAll(mapDomainProperties);   // Merge the properties
                  if (mapGroupProperties != null)
                      properties.putAll(mapGroupProperties);   // Merge the properties
                  if (mapUserProperties != null)
                      properties.putAll(mapUserProperties);   // Merge the properties
                  if (recUserInfo != null)
                  {
                      this.setProperty(DBParams.USER_ID, recUserInfo.getField(UserInfoModel.ID).toString());
                      if (!recUserInfo.getField(UserInfoModel.USER_NAME).isNull())
                          this.setProperty(DBParams.USER_NAME, recUserInfo.getField(UserInfoModel.USER_NAME).toString());
                      if (properties != null)
                      {
                          properties.put(DBParams.USER_ID, recUserInfo.getField(UserInfo.ID).toString());
                          if (!recUserInfo.getField(UserInfoModel.USER_NAME).isNull())
                              properties.put(DBParams.USER_NAME, recUserInfo.getField(UserInfoModel.USER_NAME).toString());
                          if (this.getProperty(DBParams.AUTH_TOKEN) != null)
                              properties.put(DBParams.AUTH_TOKEN, this.getProperty(DBParams.AUTH_TOKEN));
                      }
                  }
                  m_systemRecordOwner.setProperties(properties);  // These are the user properties
  
                  UserLogModel recUserLog = null;
                  if (m_systemRecordOwner != null)
                  {   // Always
                      recUserLog = (UserLogModel)m_systemRecordOwner.getRecord(UserLogModel.USER_LOG_FILE);
                      if (recUserLog == null)
                          recUserLog = (UserLogModel)Record.makeRecordFromClassName(UserLogModel.THICK_CLASS, m_systemRecordOwner);
                  }
                  if (recUserLog != null)
                      if (this.getProperty(DBParams.USER_ID) != null)
                          if (this.getProperty(DBParams.USER_ID).length() > 0)
                              if (!DBConstants.ANON_USER_ID.equals(this.getProperty(DBParams.USER_ID)))
                  {
                      try {
                          int iUserID = Integer.parseInt(this.getProperty(DBParams.USER_ID));
                          int iUserLogTypeID = UserLogTypeModel.LOGIN;
                          String strMessage = "Login";
                          recUserLog.log(iUserID, iUserLogTypeID, strMessage);
                      } catch (NumberFormatException e) {
                          // Ignore
                      }
                  }
  
                  return Constants.NORMAL_RETURN;
              }
              else if (task != null)
                  return task.setLastError(this.getString("User name and password do not match"));
              else
                  return DBConstants.ERROR_RETURN;
          }
      }