1 #include "otsdaq/WebUsersUtilities/WebUsers.h" 
    2 #include "otsdaq/XmlUtilities/HttpXmlDocument.h" 
    4 #include <openssl/sha.h> 
   18 #define WEB_LOGIN_BKUP_DB_PATH              "bkup/" 
   20 #define SECURITY_FILE_NAME                  std::string(__ENV__("SERVICE_DATA_PATH")) + "/OtsWizardData/security.dat"
 
   22 #define USERS_ACTIVE_SESSIONS_FILE          USERS_DB_PATH + "/activeSessions.sv" 
   24 #define HASHES_DB_FILE                      HASHES_DB_PATH + "/hashes.xml" 
   25 #define USERS_DB_FILE                       USERS_DB_PATH + "/users.xml" 
   26 #define USERS_GLOBAL_HISTORY_FILE           "__global" 
   27 #define USERS_LOGIN_HISTORY_FILETYPE        "hist" 
   28 #define USERS_PREFERENCES_FILETYPE          "pref" 
   29 #define SYSTEM_PREFERENCES_PREFIX           "system.preset" 
   30 #define USER_WITH_LOCK_FILE                 WEB_LOGIN_DB_PATH + "/user_with_lock.dat" 
   31 #define IP_BLACKLIST_FILE                   WEB_LOGIN_DB_PATH + "/ip_generated_blacklist.dat" 
   32 #define IP_REJECT_FILE                      WEB_LOGIN_DB_PATH + "/ip_reject.dat" 
   33 #define IP_ACCEPT_FILE                      WEB_LOGIN_DB_PATH + "/ip_accept.dat" 
   35 #define SILENCE_ALL_TOOLTIPS_FILENAME       "silenceTooltips" 
   37 #define HASHES_DB_GLOBAL_STRING             "hashData" 
   38 #define HASHES_DB_ENTRY_STRING              "hashEntry" 
   39 #define USERS_DB_GLOBAL_STRING              "userData" 
   40 #define USERS_DB_ENTRY_STRING               "userEntry" 
   41 #define USERS_DB_NEXT_UID_STRING            "nextUserId" 
   44 #define PREF_XML_BGCOLOR_FIELD              "pref_bgcolor"       
   45 #define PREF_XML_DBCOLOR_FIELD              "pref_dbcolor"       
   46 #define PREF_XML_WINCOLOR_FIELD             "pref_wincolor"      
   47 #define PREF_XML_LAYOUT_FIELD               "pref_layout"        
   48 #define PREF_XML_SYSLAYOUT_FIELD            "pref_syslayout"     
   49 #define PREF_XML_PERMISSIONS_FIELD          "desktop_user_permissions"   
   50 #define PREF_XML_USERLOCK_FIELD             "username_with_lock"         
   51 #define PREF_XML_USERNAME_FIELD             "pref_username"      
   52 #define PREF_XML_OTS_OWNER_FIELD            "ots_owner"          
   54 #define PREF_XML_BGCOLOR_DEFAULT            "rgb(0,76,151)"      
   55 #define PREF_XML_DBCOLOR_DEFAULT            "rgb(0,40,85)"       
   56 #define PREF_XML_WINCOLOR_DEFAULT           "rgba(196,229,255,0.9)"      
   57 #define PREF_XML_LAYOUT_DEFAULT             "0;0;0;0"            
   58 #define PREF_XML_SYSLAYOUT_DEFAULT          "0;0"                
   60 #define PREF_XML_ACCOUNTS_FIELD             "users_accounts"     
   61 #define PREF_XML_LOGIN_HISTORY_FIELD        "login_entry"        
   64 const std::string WebUsers::DEFAULT_ADMIN_USERNAME          = 
"admin";
 
   65 const std::string WebUsers::DEFAULT_ADMIN_DISPLAY_NAME      = 
"Administrator";
 
   66 const std::string WebUsers::DEFAULT_ADMIN_EMAIL             = 
"root@otsdaq.fnal.gov";
 
   67 const std::string WebUsers::DEFAULT_ITERATOR_USERNAME       = 
"iterator";
 
   68 const std::string WebUsers::DEFAULT_STATECHANGER_USERNAME   = 
"statechanger";
 
   69 const std::string WebUsers::DEFAULT_USER_GROUP              = 
"allUsers";
 
   71 const std::string WebUsers::REQ_NO_LOGIN_RESPONSE           = 
"NoLogin";
 
   72 const std::string WebUsers::REQ_NO_PERMISSION_RESPONSE      = 
"NoPermission";
 
   73 const std::string WebUsers::REQ_USER_LOCKOUT_RESPONSE       = 
"UserLockout";
 
   74 const std::string WebUsers::REQ_LOCK_REQUIRED_RESPONSE      = 
"LockRequired";
 
   75 const std::string WebUsers::REQ_ALLOW_NO_USER               = 
"AllowNoUser";
 
   77 const std::string WebUsers::SECURITY_TYPE_NONE              = 
"NoSecurity";
 
   78 const std::string WebUsers::SECURITY_TYPE_DIGEST_ACCESS     = 
"DigestAccessAuthentication";
 
   79 const std::string WebUsers::SECURITY_TYPE_DEFAULT           = WebUsers::SECURITY_TYPE_NONE; 
 
   81 const std::vector<std::string> WebUsers::HashesDatabaseEntryFields_ = {
"hash",
"lastAccessTime"};
 
   82 const std::vector<std::string> WebUsers::UsersDatabaseEntryFields_ = {
"username",
"displayName",
"salt",
 
   83         "uid",
"permissions",
"lastLoginAttemptTime",
"accountCreatedTime",
 
   84         "loginFailureCount",
"lastModifiedTime",
"lastModifierUsername",
"useremail"};
 
   87 #define __MF_SUBJECT__ "WebUsers" 
   90 volatile bool       WebUsers::CareAboutCookieCodes_             = 
true;
 
  100     usersNextUserId_       = 0;   
 
  101     usersUsernameWithLock_ = 
"";  
 
  120     mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
 
  121     mkdir(((std::string)WEB_LOGIN_DB_PATH + 
"bkup/" + USERS_DB_PATH).c_str(), 0755);
 
  122     mkdir(((std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH).c_str(), 0755);
 
  123     mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
 
  124     mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH).c_str(), 0755);
 
  125     mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH).c_str(), 0755);
 
  128         __COUT__ << 
"FATAL USER DATABASE ERROR - failed to load!!!" << __E__;
 
  130     loadSecuritySelection();
 
  134     std::string user = DEFAULT_ADMIN_USERNAME;
 
  135     if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
 
  137         __SS__ << 
"user: " << user << 
" is not found. This should be impossible!" 
  139         __COUT_ERR__ << ss.str();
 
  142     else if(Users_[i].salt_ ==
 
  144             securityType_ == SECURITY_TYPE_DIGEST_ACCESS)
 
  150             [](
const std::string& nac, 
const std::string& user) {
 
  151                 WebUsers::NACDisplayThread(nac, user);
 
  153             Users_[i].getNewAccountCode(),
 
  167     __COUT__ << 
"Done with Web Users initialization!" << __E__;
 
  176                                    std::ostringstream*        out,
 
  180     std::lock_guard<std::mutex> lock(webUserMutex_);
 
  188            userInfo.cookieCode_,
 
  189            &userInfo.groupPermissionLevelMap_,
 
  192            !userInfo.automatedCommand_ ,
 
  196            &userInfo.usernameWithLock_,
 
  199         *out << userInfo.cookieCode_;
 
  200         goto HANDLE_ACCESS_FAILURE;  
 
  206     i = searchUsersDatabaseForUserId(userInfo.uid_);
 
  207     if(i >= Users_.size())
 
  209         __SS__ << 
"Illegal uid encountered in cookie codes!? " << i << __E__;
 
  210         ss << 
"User size = " << Users_.size() << __E__;
 
  214     userInfo.username_    = Users_[i].username_;
 
  215     userInfo.displayName_ = Users_[i].displayName_;
 
  218         goto HANDLE_ACCESS_FAILURE;  
 
  222 HANDLE_ACCESS_FAILURE:
 
  224     if(!userInfo.automatedCommand_)
 
  225         __COUT_ERR__ << 
"Failed request (requestType = " << userInfo.requestType_
 
  226                      << 
"): " << out->str() << __E__;
 
  237     userInfo.ip_ = cgi.getEnvironment().getRemoteAddr();
 
  240     userInfo.username_         = 
"";
 
  241     userInfo.displayName_      = 
"";
 
  242     userInfo.usernameWithLock_ = 
"";
 
  256                                   std::ostringstream*        out,
 
  260                                   const std::string& wizardModeSequence )
 
  266     if(userInfo.requireSecurity_ && userInfo.permissionsThreshold_ > 1)
 
  272         if(isWizardMode && wizardModeSequence.size() < 8)
 
  275             *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
 
  276             __COUT__ << 
"User (@" << userInfo.ip_ << 
") has attempted requestType '" 
  277                      << userInfo.requestType_
 
  278                      << 
"' which requires sufficient security enabled. Please enable the " 
  280                         " sequence of at least 8 characters." 
  285         else if(!isWizardMode &&
 
  286                 (userInfo.username_ == WebUsers::DEFAULT_ADMIN_USERNAME ||
 
  287                  userInfo.username_ == WebUsers::DEFAULT_ITERATOR_USERNAME ||
 
  288                  userInfo.username_ == WebUsers::DEFAULT_STATECHANGER_USERNAME))
 
  291             *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
 
  292             __COUT__ << 
"User (@" << userInfo.ip_ << 
") has attempted requestType '" 
  293                      << userInfo.requestType_
 
  294                      << 
"' which requires sufficient security enabled. Please enable " 
  296                         " logins (Note: the user admin is disallowed in an attempt to " 
  297                         "force personal accountability for edits)." 
  305     if(!userInfo.automatedCommand_)
 
  307         __COUTT__ << 
"requestType ==========>>> " << userInfo.requestType_ << __E__;
 
  308         __COUTTV__((
unsigned int)userInfo.permissionLevel_);
 
  309         __COUTTV__((
unsigned int)userInfo.permissionsThreshold_);
 
  313     if(!isWizardMode && !userInfo.allowNoUser_ &&
 
  314        userInfo.cookieCode_.length() != WebUsers::COOKIE_CODE_LENGTH &&
 
  316          userInfo.cookieCode_ ==
 
  320         __COUT__ << 
"User (@" << userInfo.ip_
 
  321                  << 
") has invalid cookie code: " << userInfo.cookieCode_ << std::endl;
 
  322         *out << WebUsers::REQ_NO_LOGIN_RESPONSE;
 
  326     if(!userInfo.allowNoUser_ &&
 
  327        (userInfo.permissionLevel_ == 0 ||       
 
  328         userInfo.permissionsThreshold_ == 0 ||  
 
  329         userInfo.permissionLevel_ < userInfo.permissionsThreshold_))
 
  332         *out << WebUsers::REQ_NO_PERMISSION_RESPONSE;
 
  333         __COUT_INFO__ << 
"User (@" << userInfo.ip_
 
  334                       << 
") has insufficient permissions for requestType '" 
  335                       << userInfo.requestType_ << 
"' : user level is " 
  336                       << (
unsigned int)userInfo.permissionLevel_ << 
", " 
  337                       << (
unsigned int)userInfo.permissionsThreshold_ << 
" required." 
  345         userInfo.username_         = WebUsers::DEFAULT_ADMIN_USERNAME;
 
  346         userInfo.displayName_      = 
"Admin";
 
  347         userInfo.usernameWithLock_ = userInfo.username_;
 
  355         if(userInfo.allowNoUser_)
 
  356             xmldoc->setHeader(WebUsers::REQ_ALLOW_NO_USER);
 
  358             xmldoc->setHeader(userInfo.cookieCode_);
 
  361     if(userInfo.allowNoUser_)
 
  363         if(userInfo.automatedCommand_)
 
  364             __COUTT__ << 
"Allowing anonymous access." << __E__;
 
  375     if((userInfo.checkLock_ || userInfo.requireLock_) &&
 
  376        userInfo.usernameWithLock_ != 
"" &&
 
  377        userInfo.usernameWithLock_ != userInfo.username_)
 
  379         *out << WebUsers::REQ_USER_LOCKOUT_RESPONSE;
 
  380         __COUT_INFO__ << 
"User '" << userInfo.username_ << 
"' is locked out. '" 
  381                       << userInfo.usernameWithLock_ << 
"' has lock." << std::endl;
 
  385     if(userInfo.requireLock_ && userInfo.usernameWithLock_ != userInfo.username_)
 
  387         *out << WebUsers::REQ_LOCK_REQUIRED_RESPONSE;
 
  388         __COUT_INFO__ << 
"User '" << userInfo.username_
 
  389                       << 
"' must have lock to proceed. ('" << userInfo.usernameWithLock_
 
  390                       << 
"' has lock.)" << std::endl;
 
  406     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
 
  407     __COUT__ << fn << __E__;
 
  409     FILE* fp = fopen(fn.c_str(), 
"w");
 
  412         __COUT_ERR__ << 
"Error! Persistent active sessions could not be saved to file: " 
  418     fprintf(fp, 
"%d\n", version);
 
  419     for(
unsigned int i = 0; i < ActiveSessions_.size(); ++i)
 
  427         fprintf(fp, 
"%s\n", ActiveSessions_[i].cookieCode_.c_str());
 
  428         fprintf(fp, 
"%s\n", ActiveSessions_[i].ip_.c_str());
 
  429         fprintf(fp, 
"%lu\n", ActiveSessions_[i].userId_);
 
  430         fprintf(fp, 
"%lu\n", ActiveSessions_[i].sessionIndex_);
 
  431         fprintf(fp, 
"%ld\n", ActiveSessions_[i].startTime_);
 
  434     __COUT__ << 
"Active Sessions saved with size " << ActiveSessions_.size() << __E__;
 
  446     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_ACTIVE_SESSIONS_FILE;
 
  447     __COUT__ << fn << __E__;
 
  448     FILE* fp = fopen(fn.c_str(), 
"r");
 
  452             << 
"Persistent active sessions were not found to be loaded at file: " << fn
 
  459     const int LINELEN = 1000;
 
  461     fgets(line, LINELEN, fp);
 
  462     sscanf(line, 
"%d", &version);
 
  465         __COUT__ << 
"Extracting active sessions..." << __E__;
 
  467     while(fgets(line, LINELEN, fp))
 
  470             line[strlen(line) - 1] = 
'\0';  
 
  471         if(strlen(line) != COOKIE_CODE_LENGTH)
 
  473             __COUT__ << 
"Illegal cookie code found: " << line << __E__;
 
  479         ActiveSessions_.back().cookieCode_ = line;
 
  481         fgets(line, LINELEN, fp);
 
  483             line[strlen(line) - 1] = 
'\0';  
 
  484         ActiveSessions_.back().ip_ = line;
 
  486         fgets(line, LINELEN, fp);
 
  487         sscanf(line, 
"%lu", &(ActiveSessions_.back().userId_));
 
  489         fgets(line, LINELEN, fp);
 
  490         sscanf(line, 
"%lu", &(ActiveSessions_.back().sessionIndex_));
 
  492         fgets(line, LINELEN, fp);
 
  493         sscanf(line, 
"%ld", &(ActiveSessions_.back().startTime_));
 
  496     __COUT__ << 
"Active Sessions loaded with size " << ActiveSessions_.size() << __E__;
 
  500     fp = fopen(fn.c_str(), 
"w");
 
  509 bool WebUsers::loadDatabases()
 
  514     const unsigned int LINE_LEN = 1000;
 
  516     unsigned int       i, si, c, len, f;
 
  527     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_FILE;
 
  528     __COUT__ << fn << __E__;
 
  529     fp = fopen(fn.c_str(), 
"r");
 
  532         mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str(),
 
  534         __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)HASHES_DB_PATH).c_str()
 
  536         fp = fopen(fn.c_str(), 
"w");
 
  539         __COUT__ << 
"Hashes database created: " << fn << __E__;
 
  541         saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, 
"", DB_SAVE_OPEN);
 
  542         saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, 
"", DB_SAVE_CLOSE);
 
  549         while(fgets(line, LINE_LEN, fp))
 
  551             if(strlen(line) < SHA512_DIGEST_LENGTH)
 
  557             for(i = 0; i < len; ++i)
 
  565                     while(i < len && line[i] != 
'<')
 
  577                         Hashes_.push_back(Hash());
 
  578                         Hashes_.back().hash_ = &line[si];
 
  581                         sscanf(&line[si], 
"%ld", &Hashes_.back().accessTime_);
 
  584         __COUT__ << Hashes_.size() << 
" Hashes found." << __E__;
 
  598     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_FILE;
 
  599     fp = fopen(fn.c_str(), 
"r");
 
  602         mkdir(((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str(),
 
  604         __COUT__ << ((std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_DB_PATH).c_str()
 
  606         fp = fopen(fn.c_str(), 
"w");
 
  609         __COUT__ << 
"Users database created: " << fn << __E__;
 
  611         saveToDatabase(fp, USERS_DB_GLOBAL_STRING, 
"", DB_SAVE_OPEN);
 
  613         sprintf(nidStr, 
"%lu", usersNextUserId_);
 
  614         saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, nidStr, DB_SAVE_OPEN_AND_CLOSE);
 
  615         saveToDatabase(fp, USERS_DB_GLOBAL_STRING, 
"", DB_SAVE_CLOSE);
 
  619                          DEFAULT_ADMIN_DISPLAY_NAME,
 
  620                          DEFAULT_ADMIN_EMAIL);  
 
  624         __COUT__ << 
"Users database: " << fn << __E__;
 
  628         char salt[] = 
"nextUserId";
 
  629         while(fgets(line, LINE_LEN, fp))
 
  631             if(strlen(line) < strlen(salt) * 2)
 
  634             for(i = 0; i < strlen(salt); ++i)  
 
  635                 if(line[i + 1] != salt[i])
 
  638             if(i == strlen(salt))  
 
  643                 while(i < LINE_LEN && line[i] != 
'\0' && line[i] != 
'<')
 
  646                 sscanf(&line[si], 
"%lu", &usersNextUserId_);
 
  651         __COUT__ << 
"Found Users database next user Id: " << usersNextUserId_ << __E__;
 
  655         while(fgets(line, LINE_LEN, fp))
 
  657             if(strlen(line) < 30)
 
  665                 __COUT__ << 
"Line buffer too small: " << len << __E__;
 
  671             for(i = 0; i < len; ++i)
 
  675                     if(c == 0 || c % 2 == 1)
 
  679                     while(i < len && line[i] != 
'<')
 
  691                         Users_.push_back(User());
 
  692                         Users_.back().username_ = &line[si];
 
  695                         Users_.back().displayName_ = &line[si];
 
  697                         Users_.back().salt_ = &line[si];
 
  699                         sscanf(&line[si], 
"%lu", &Users_.back().userId_);
 
  702                         std::map<std::string, permissionLevel_t>& lastPermissionsMap =
 
  703                             Users_.back().permissions_;
 
  704                         StringMacros::getMapFromString<permissionLevel_t>(
 
  705                             &line[si], lastPermissionsMap);
 
  713                         if(lastPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) ==
 
  714                            lastPermissionsMap.end())
 
  717                                 << 
"User '" << Users_.back().username_
 
  718                                 << 
"' is not a member of the default user group '" 
  719                                 << WebUsers::DEFAULT_USER_GROUP
 
  720                                 << 
".' Assuming user account is inactive (permission " 
  722                                 << WebUsers::PERMISSION_LEVEL_INACTIVE << 
")." << __E__;
 
  723                             lastPermissionsMap[WebUsers::DEFAULT_USER_GROUP] =
 
  724                                 WebUsers::PERMISSION_LEVEL_INACTIVE;  
 
  727                         if(Users_.back().username_ == DEFAULT_ADMIN_USERNAME)
 
  731                             std::map<std::string ,
 
  732                                      WebUsers::permissionLevel_t>
 
  733                                 initPermissions = {{WebUsers::DEFAULT_USER_GROUP,
 
  736                             Users_.back().permissions_ = initPermissions;
 
  740                         sscanf(&line[si], 
"%ld", &Users_.back().lastLoginAttempt_);
 
  742                         sscanf(&line[si], 
"%ld", &Users_.back().accountCreationTime_);
 
  744                         sscanf(&line[si], 
"%hhu", &Users_.back().loginFailureCount_);
 
  746                         sscanf(&line[si], 
"%ld", &Users_.back().accessModifierTime());
 
  748                         Users_.back().loadModifierUsername(&line[si]);
 
  750                         Users_.back().email_ = &line[si];
 
  757     __COUT__ << Users_.size() << 
" Users found." << __E__;
 
  758     for(
size_t ii = 0; ii < Users_.size(); ++ii)
 
  761             "User [" << Users_[ii].userId_ << 
"] \tName: " << std::left
 
  762                   << std::setfill(
' ') << std::setw(20) << Users_[ii].username_
 
  763                   << 
"\tDisplay Name: " << std::left << std::setfill(
' ') << std::setw(30)
 
  764                   << Users_[ii].displayName_ << 
"\tEmail: " << std::left
 
  765                   << std::setfill(
' ') << std::setw(30) << Users_[ii].email_
 
  766                   << 
"\tNAC: " << std::left << std::setfill(
' ') << std::setw(5)
 
  767                   << Users_[ii].getNewAccountCode()
 
  768                   << 
"\tFailedCount: " << (int)Users_[ii].loginFailureCount_
 
  787 void WebUsers::saveToDatabase(FILE*              fp,
 
  788                               const std::string& field,
 
  789                               const std::string& value,
 
  796     std::string newLine = addNewLine ? 
"\n" : 
"";
 
  798     if(type == DB_SAVE_OPEN_AND_CLOSE)
 
  805     else if(type == DB_SAVE_OPEN)
 
  806         fprintf(fp, 
"<%s>%s%s", field.c_str(), value.c_str(), newLine.c_str());
 
  807     else if(type == DB_SAVE_CLOSE)
 
  808         fprintf(fp, 
"</%s>%s", field.c_str(), newLine.c_str());
 
  817 bool WebUsers::saveDatabaseToFile(uint8_t db)
 
  822         (std::string)WEB_LOGIN_DB_PATH +
 
  823         ((db == DB_USERS) ? (std::string)USERS_DB_FILE : (std::string)HASHES_DB_FILE);
 
  825     __COUT__ << 
"Save Database Filename: " << fn << __E__;
 
  831         sprintf(dayAppend, 
".%lu.bkup", time(0) / (3600 * 24));
 
  832         std::string bkup_fn = (std::string)WEB_LOGIN_DB_PATH +
 
  833                               (std::string)WEB_LOGIN_BKUP_DB_PATH +
 
  834                               ((db == DB_USERS) ? (std::string)USERS_DB_FILE
 
  835                                                 : (std::string)HASHES_DB_FILE) +
 
  836                               (std::string)dayAppend;
 
  838         __COUT__ << 
"Backup file: " << bkup_fn << __E__;
 
  840         std::string shell_command = 
"mv " + fn + 
" " + bkup_fn;
 
  841         system(shell_command.c_str());
 
  844     FILE* fp = fopen(fn.c_str(), 
"wb");  
 
  852         saveToDatabase(fp, USERS_DB_GLOBAL_STRING, 
"", DB_SAVE_OPEN);
 
  854         sprintf(fldStr, 
"%lu", usersNextUserId_);
 
  855         saveToDatabase(fp, USERS_DB_NEXT_UID_STRING, fldStr, DB_SAVE_OPEN_AND_CLOSE);
 
  857         __COUT__ << 
"Saving " << Users_.size() << 
" Users." << __E__;
 
  859         for(uint64_t i = 0; i < Users_.size(); ++i)
 
  863             saveToDatabase(fp, USERS_DB_ENTRY_STRING, 
"", DB_SAVE_OPEN, 
false);
 
  865             for(
unsigned int f = 0; f < WebUsers::UsersDatabaseEntryFields_.size(); ++f)
 
  870                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  872                                    DB_SAVE_OPEN_AND_CLOSE,
 
  876                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  877                                    Users_[i].displayName_,
 
  878                                    DB_SAVE_OPEN_AND_CLOSE,
 
  882                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  884                                    DB_SAVE_OPEN_AND_CLOSE,
 
  888                     sprintf(fldStr, 
"%lu", Users_[i].userId_);
 
  890                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  892                                    DB_SAVE_OPEN_AND_CLOSE,
 
  897                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  901                                    DB_SAVE_OPEN_AND_CLOSE,
 
  905                     sprintf(fldStr, 
"%lu", Users_[i].lastLoginAttempt_);
 
  907                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  909                                    DB_SAVE_OPEN_AND_CLOSE,
 
  914                     sprintf(fldStr, 
"%lu", Users_[i].accountCreationTime_);
 
  916                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  918                                    DB_SAVE_OPEN_AND_CLOSE,
 
  923                     sprintf(fldStr, 
"%hhu", Users_[i].loginFailureCount_);
 
  925                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  927                                    DB_SAVE_OPEN_AND_CLOSE,
 
  932                     sprintf(fldStr, 
"%lu", Users_[i].getModifierTime());
 
  934                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  936                                    DB_SAVE_OPEN_AND_CLOSE,
 
  941                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  942                                    Users_[i].getModifierUsername(),
 
  943                                    DB_SAVE_OPEN_AND_CLOSE,
 
  947                                    WebUsers::UsersDatabaseEntryFields_[f],
 
  949                                    DB_SAVE_OPEN_AND_CLOSE,
 
  953             saveToDatabase(fp, USERS_DB_ENTRY_STRING, 
"", DB_SAVE_CLOSE);
 
  956         saveToDatabase(fp, USERS_DB_GLOBAL_STRING, 
"", DB_SAVE_CLOSE);
 
  960         saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, 
"", DB_SAVE_OPEN);
 
  962         __COUT__ << 
"Saving " << Hashes_.size() << 
" Hashes." << __E__;
 
  963         for(uint64_t i = 0; i < Hashes_.size(); ++i)
 
  965             __COUT__ << 
"Saving " << Hashes_[i].hash_ << 
" Hash." << __E__;
 
  966             saveToDatabase(fp, HASHES_DB_ENTRY_STRING, 
"", DB_SAVE_OPEN, 
false);
 
  967             for(
unsigned int f = 0; f < WebUsers::HashesDatabaseEntryFields_.size(); ++f)
 
  971                                    WebUsers::HashesDatabaseEntryFields_[f],
 
  973                                    DB_SAVE_OPEN_AND_CLOSE,
 
  977                     sprintf(fldStr, 
"%lu", Hashes_[i].accessTime_);
 
  979                                    WebUsers::HashesDatabaseEntryFields_[f],
 
  981                                    DB_SAVE_OPEN_AND_CLOSE,
 
  985             saveToDatabase(fp, HASHES_DB_ENTRY_STRING, 
"", DB_SAVE_CLOSE);
 
  988         saveToDatabase(fp, HASHES_DB_GLOBAL_STRING, 
"", DB_SAVE_CLOSE);
 
 1004                                 const std::string& displayName,
 
 1005                                 const std::string& email)
 
 1007     __COUT__ << 
"Creating account: " << username << __E__;
 
 1010     if((i = searchUsersDatabaseForUsername(username)) != NOT_FOUND_IN_DATABASE ||
 
 1011        username == WebUsers::DEFAULT_ITERATOR_USERNAME ||
 
 1012        username == WebUsers::DEFAULT_STATECHANGER_USERNAME)  
 
 1015         __SS__ << 
"Username '" << username
 
 1016                << 
"' already exists! Please choose a unique username." << __E__;
 
 1021     if((i = searchUsersDatabaseForDisplayName(displayName)) != NOT_FOUND_IN_DATABASE)
 
 1024         __SS__ << 
"Display Name '" << displayName
 
 1025                << 
"' already exists! Please choose a unique display name." << __E__;
 
 1030     Users_.push_back(
User());
 
 1032     Users_.back().username_    = username;
 
 1033     Users_.back().displayName_ = displayName;
 
 1034     Users_.back().email_       = email;
 
 1037     std::map<std::string , WebUsers::permissionLevel_t> initPermissions = {
 
 1038         {WebUsers::DEFAULT_USER_GROUP,
 
 1039          (Users_.size() ? WebUsers::PERMISSION_LEVEL_NOVICE
 
 1042     Users_.back().permissions_ = initPermissions;
 
 1043     Users_.back().userId_      = usersNextUserId_++;
 
 1044     if(usersNextUserId_ >= ACCOUNT_ERROR_THRESHOLD)  
 
 1046         __SS__ << 
"usersNextUserId_ wrap around!! Too many users??? Notify Admins." 
 1049         usersNextUserId_ = 1;  
 
 1053     Users_.back().accountCreationTime_ = time(0);
 
 1055     if(!saveDatabaseToFile(DB_USERS))
 
 1057         __SS__ << 
"Failed to save User DB!" << __E__;
 
 1068 bool WebUsers::deleteAccount(
const std::string& username, 
const std::string& displayName)
 
 1070     uint64_t i = searchUsersDatabaseForUsername(username);
 
 1071     if(i == NOT_FOUND_IN_DATABASE)
 
 1073     if(Users_[i].displayName_ != displayName)
 
 1078     Users_.erase(Users_.begin() + i);
 
 1081     return saveDatabaseToFile(DB_USERS);
 
 1085 unsigned int WebUsers::hexByteStrToInt(
const char* h)
 
 1088     char         hs[3] = {h[0], h[1], 
'\0'};
 
 1089     sscanf(hs, 
"%X", &rv);
 
 1094 void WebUsers::intToHexStr(
unsigned char i, 
char* h) { sprintf(h, 
"%2.2X", i); }
 
 1107                                         std::string&       jumbledUser,
 
 1108                                         const std::string& jumbledPw,
 
 1109                                         std::string&       newAccountCode,
 
 1110                                         const std::string& ip)
 
 1115         __COUT_ERR__ << 
"rejected ip: " << ip << __E__;
 
 1116         return ACCOUNT_BLACKLISTED;
 
 1121     if(!CareAboutCookieCodes_)  
 
 1125         newAccountCode = genCookieCode();  
 
 1132     if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
 
 1134         __COUT_ERR__ << 
"uuid: " << uuid << 
" is not found" << __E__;
 
 1135         newAccountCode = 
"1";  
 
 1137         incrementIpBlacklistCount(ip);  
 
 1139         return NOT_FOUND_IN_DATABASE;
 
 1141     ++LoginSessions_[i].loginAttempts_;
 
 1143     std::string user = dejumble(jumbledUser, LoginSessions_[i].id_);
 
 1145     std::string pw = dejumble(jumbledPw, LoginSessions_[i].id_);
 
 1148     if((i = searchUsersDatabaseForUsername(user)) == NOT_FOUND_IN_DATABASE)
 
 1150         __COUT_ERR__ << 
"user: " << user << 
" is not found" << __E__;
 
 1152         incrementIpBlacklistCount(ip);  
 
 1154         return NOT_FOUND_IN_DATABASE;
 
 1157         ipBlacklistCounts_[ip] = 0;  
 
 1159     Users_[i].lastLoginAttempt_ = time(0);
 
 1161     if(isInactiveForGroup(Users_[i].permissions_))
 
 1163         __COUT_ERR__ << 
"User '" << user
 
 1164                      << 
"' account INACTIVE (could be due to failed logins)" << __E__;
 
 1165         return ACCOUNT_INACTIVE;
 
 1168     if(Users_[i].salt_ == 
"")  
 
 1170         __COUT__ << 
"First login attempt for user: " << user << __E__;
 
 1172         if(newAccountCode != Users_[i].getNewAccountCode())
 
 1174             __COUT__ << 
"New account code did not match: " 
 1175                      << Users_[i].getNewAccountCode() << 
" != " << newAccountCode
 
 1177             saveDatabaseToFile(DB_USERS);  
 
 1178             return NOT_FOUND_IN_DATABASE;
 
 1184         while(!addToHashesDatabase(
 
 1185             sha512(user, pw, Users_[i].salt_)))  
 
 1190             Users_[i].salt_ = 
"";
 
 1193         __COUT__ << 
"\tHash added: " << Hashes_.back().hash_ << __E__;
 
 1197         std::string salt = Users_[i].salt_;  
 
 1199         if(searchHashesDatabaseForHash(sha512(user, pw, salt)) == NOT_FOUND_IN_DATABASE)
 
 1201             __COUT__ << 
"Failed login for " << user << 
" with permissions " 
 1205             if(++Users_[i].loginFailureCount_ != (
unsigned char)-1)
 
 1206                 ++Users_[i].loginFailureCount_;
 
 1208             if(Users_[i].loginFailureCount_ >= USERS_MAX_LOGIN_FAILURES)
 
 1209                 Users_[i].permissions_[WebUsers::DEFAULT_USER_GROUP] =
 
 1210                     WebUsers::PERMISSION_LEVEL_INACTIVE;  
 
 1212             __COUT_INFO__ << 
"User/pw for user '" << user
 
 1213                           << 
"' was not correct (Failed Attempt #" 
 1214                           << (int)Users_[i].loginFailureCount_ << 
" of " 
 1215                           << (
int)USERS_MAX_LOGIN_FAILURES << 
" allowed)." << __E__;
 
 1217             __COUTV__(isInactiveForGroup(Users_[i].permissions_));
 
 1218             if(isInactiveForGroup(Users_[i].permissions_))
 
 1219                 __COUT_INFO__ << 
"Account '" << user
 
 1220                               << 
"' has been marked inactive due to too many failed " 
 1221                                  "login attempts (Failed Attempt #" 
 1222                               << (int)Users_[i].loginFailureCount_
 
 1223                               << 
")! Note only admins can reactivate accounts." << __E__;
 
 1225             saveDatabaseToFile(DB_USERS);  
 
 1226             return NOT_FOUND_IN_DATABASE;
 
 1230     __COUT_INFO__ << 
"Login successful for: " << user << __E__;
 
 1232     Users_[i].loginFailureCount_ = 0;
 
 1235     for(
int h = 0; h < 2; ++h)
 
 1237         std::string fn = (std::string)WEB_LOGIN_DB_PATH +
 
 1238                          (std::string)USERS_LOGIN_HISTORY_PATH +
 
 1239                          (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) + 
"." +
 
 1240                          (std::string)USERS_LOGIN_HISTORY_FILETYPE;
 
 1247                   (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
 
 1251             __COUT__ << 
"No previous login history found." << __E__;
 
 1257                     "Time=%lu Username=%s Permissions=%s UID=%lu",
 
 1259                     Users_[i].username_.c_str(),
 
 1264                     "Time=%lu displayName=%s Permissions=%s UID=%lu",
 
 1266                     Users_[i].displayName_.c_str(),
 
 1269         histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
 
 1276     saveDatabaseToFile(DB_USERS);             
 
 1277     jumbledUser    = Users_[i].displayName_;  
 
 1278     newAccountCode = createNewActiveSession(Users_[i].userId_,
 
 1281     if(ActiveSessions_.size() ==
 
 1284         __COUT__ << 
"Attempting to auto-lock for first login user '" 
 1285                  << Users_[i].username_ << 
"'... " << __E__;
 
 1289     return Users_[i].userId_;  
 
 1300                                                 std::string&       cookieCode,
 
 1302                                                 const std::string& ip)
 
 1306         __COUT_ERR__ << 
"rejected ip: " << ip << __E__;
 
 1307         return NOT_FOUND_IN_DATABASE;
 
 1312     if(!CareAboutCookieCodes_)  
 
 1316         cookieCode   = genCookieCode();  
 
 1322         __COUT__ << 
"Rejecting cert logon with blank fingerprint" << __E__;
 
 1324         incrementIpBlacklistCount(ip);  
 
 1326         return NOT_FOUND_IN_DATABASE;
 
 1332     if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
 
 1334         __COUT__ << 
"uuid: " << uuid << 
" is not found" << __E__;
 
 1337         incrementIpBlacklistCount(ip);  
 
 1339         return NOT_FOUND_IN_DATABASE;
 
 1341     ++LoginSessions_[i].loginAttempts_;
 
 1343     email = getUserEmailFromFingerprint(email);
 
 1344     __COUT__ << 
"DejumbledEmail = " << email << __E__;
 
 1347         __COUT__ << 
"Rejecting logon with unknown fingerprint" << __E__;
 
 1349         incrementIpBlacklistCount(ip);  
 
 1351         return NOT_FOUND_IN_DATABASE;
 
 1355     if((i = searchUsersDatabaseForUserEmail(email)) == NOT_FOUND_IN_DATABASE)
 
 1357         __COUT__ << 
"email: " << email << 
" is not found" << __E__;
 
 1359         incrementIpBlacklistCount(ip);  
 
 1361         return NOT_FOUND_IN_DATABASE;
 
 1364         ipBlacklistCounts_[ip] = 0;  
 
 1368     Users_[i].lastLoginAttempt_ = time(0);
 
 1369     if(isInactiveForGroup(Users_[i].permissions_))
 
 1371         __COUT__ << 
"User '" << user
 
 1372                  << 
"' account INACTIVE (could be due to failed logins)." << __E__;
 
 1373         return NOT_FOUND_IN_DATABASE;
 
 1376     if(Users_[i].salt_ == 
"")  
 
 1378         return NOT_FOUND_IN_DATABASE;
 
 1381     __COUT__ << 
"Login successful for: " << user << __E__;
 
 1383     Users_[i].loginFailureCount_ = 0;
 
 1386     for(
int h = 0; h < 2; ++h)
 
 1388         std::string fn = (std::string)WEB_LOGIN_DB_PATH +
 
 1389                          (std::string)USERS_LOGIN_HISTORY_PATH +
 
 1390                          (h ? USERS_GLOBAL_HISTORY_FILE : Users_[i].username_) + 
"." +
 
 1391                          (std::string)USERS_LOGIN_HISTORY_FILETYPE;
 
 1398                   (h ? USERS_GLOBAL_HISTORY_SIZE : USERS_LOGIN_HISTORY_SIZE))
 
 1402             __COUT__ << 
"No previous login history found." << __E__;
 
 1408                     "Time=%lu Username=%s Permissions=%s UID=%lu",
 
 1410                     Users_[i].username_.c_str(),
 
 1415                     "Time=%lu displayName=%s Permissions=%s UID=%lu",
 
 1417                     Users_[i].displayName_.c_str(),
 
 1420         histXml.addTextElementToData(PREF_XML_LOGIN_HISTORY_FIELD, entryStr);
 
 1427     saveDatabaseToFile(DB_USERS);         
 
 1428     email      = Users_[i].displayName_;  
 
 1429     cookieCode = createNewActiveSession(Users_[i].userId_,
 
 1431     return Users_[i].userId_;                 
 
 1437 uint64_t WebUsers::searchActiveSessionDatabaseForCookie(
 
 1438     const std::string& cookieCode)
 const 
 1441     for(; i < ActiveSessions_.size(); ++i)
 
 1442         if(ActiveSessions_[i].cookieCode_ == cookieCode)
 
 1444     return (i == ActiveSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1462 uint64_t WebUsers::checkRemoteLoginVerification(std::string&       cookieCode,
 
 1465                                                 const std::string& ip)
 
 1467     __COUTVS__(2, cookieCode);
 
 1468     remoteLoginVerificationEnabledBlackoutTime_ = 0;
 
 1469     if(!remoteLoginVerificationSocket_)  
 
 1474                 << 
"Illegal remote login verification port found in remote destination " 
 1476                 << 
". Please check remote settings." << __E__;
 
 1479         __COUT_INFO__ << 
"Instantiating Remote Gateway login verification socket! " 
 1480                          "Validation requests will go to " 
 1484         remoteLoginVerificationSocket_ =
 
 1485             std::make_unique<TransceiverSocket>(remoteLoginVerificationIP_);
 
 1486         remoteLoginVerificationSocket_->initialize();
 
 1488         remoteLoginVerificationSocketTarget_ = std::make_unique<Socket>(
 
 1494     __COUTTV__(cookieCode);
 
 1495     __COUTTV__(RemoteSessions_.size());
 
 1496     auto it = RemoteSessions_.find(cookieCode);
 
 1497     if(it != RemoteSessions_.end())  
 
 1499         __COUTT__ << 
"cookieCode still active locally!" << __E__;
 
 1500         __COUTV__(it->second.userId_);
 
 1501         return it->second.userId_;
 
 1505     __COUTTV__(doNotGoRemote);
 
 1507         return NOT_FOUND_IN_DATABASE;
 
 1518     std::string request = 
"loginVerify," + cookieCode + 
"," + (refresh ? 
"1" : 
"0") +
 
 1524     std::string requestResponseString = remoteLoginVerificationSocket_->sendAndReceive(
 
 1525         *remoteLoginVerificationSocketTarget_, request, 10 );
 
 1526     __COUTV__(requestResponseString);
 
 1529     std::vector<std::string> rxParams =
 
 1533     if(rxParams.size() != 6)
 
 1535         __COUT__ << 
"Remote login response indicates rejected: " << rxParams.size()
 
 1537         return NOT_FOUND_IN_DATABASE;
 
 1549     __COUTTV__(rxParams[2]);             
 
 1550     __COUTTV__(usersUsernameWithLock_);  
 
 1553     std::string username = rxParams[3];
 
 1554     __COUTTV__(username);
 
 1555     uint64_t j = searchUsersDatabaseForUsername(username);
 
 1556     if(j == NOT_FOUND_IN_DATABASE)
 
 1558         __COUT_INFO__ << 
"Creating User entry for remote user '" << username
 
 1559                       << 
"' in local user list to track user preferences." << __E__;
 
 1563         j = Users_.size() - 1;
 
 1566     Users_[j].lastLoginAttempt_ = time(0);
 
 1567     Users_[j].setModifier(
"REMOTE_GATEWAY");
 
 1572         .permissions_.clear();  
 
 1574                                    Users_[j].permissions_);
 
 1576     __COUTV__(Users_[j].username_);
 
 1577     __COUTV__(Users_[j].userId_);
 
 1581     cookieCode = rxParams[0];  
 
 1582     __COUTTV__(cookieCode);
 
 1583     ActiveSession& newRemoteSession =
 
 1584         RemoteSessions_[cookieCode];  
 
 1585     newRemoteSession.cookieCode_ = cookieCode;
 
 1586     newRemoteSession.ip_         = ip;
 
 1587     newRemoteSession.userId_     = Users_[j].userId_;
 
 1588     sscanf(rxParams[5].c_str(), 
"%lu", &newRemoteSession.sessionIndex_);
 
 1589     newRemoteSession.startTime_ = time(0);
 
 1592     if(!CareAboutCookieCodes_ && refresh &&
 
 1593        usersUsernameWithLock_ == DEFAULT_ADMIN_USERNAME &&
 
 1594        usersUsernameWithLock_ != username)
 
 1596         __COUT_INFO__ << 
"Overriding local user-with-lock '" << usersUsernameWithLock_
 
 1597                       << 
"' with remote user-with-lock 'Remote:" << username << 
"'" 
 1599         usersUsernameWithLock_ =
 
 1603             getUserWithLock() + 
" has locked REMOTE ots (overriding anonymous " +
 
 1604                 DEFAULT_ADMIN_USERNAME + 
" user).");
 
 1607     __COUTT__ << 
"Returning remote login success" << __E__;
 
 1608     return Users_[j].userId_;
 
 1617     if((u = searchUsersDatabaseForUsername(username)) == NOT_FOUND_IN_DATABASE)
 
 1627     __COUTT__ << 
"isUserIdActive? " << uid << __E__;
 
 1630         for(
const auto& remoteSession : RemoteSessions_)
 
 1631             if(remoteSession.second.userId_ == uid)
 
 1636     for(; i < ActiveSessions_.size(); ++i)
 
 1637         if(ActiveSessions_[i].userId_ == uid)
 
 1645 uint64_t WebUsers::searchUsersDatabaseForUsername(
const std::string& username)
 const 
 1648     for(; i < Users_.size(); ++i)
 
 1649         if(Users_[i].username_ == username)
 
 1651     return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1657 uint64_t WebUsers::searchUsersDatabaseForDisplayName(
const std::string& displayName)
 const 
 1660     for(; i < Users_.size(); ++i)
 
 1661         if(Users_[i].displayName_ == displayName)
 
 1663     return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1669 uint64_t WebUsers::searchUsersDatabaseForUserEmail(
const std::string& useremail)
 const 
 1672     for(; i < Users_.size(); ++i)
 
 1673         if(Users_[i].email_ == useremail)
 
 1675     return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1681 uint64_t WebUsers::searchUsersDatabaseForUserId(uint64_t uid)
 const 
 1684     for(; i < Users_.size(); ++i)
 
 1685         if(Users_[i].userId_ == uid)
 
 1687     return (i == Users_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1693 uint64_t WebUsers::searchLoginSessionDatabaseForUUID(
const std::string& uuid)
 const 
 1696     for(; i < LoginSessions_.size(); ++i)
 
 1697         if(LoginSessions_[i].uuid_ == uuid)
 
 1699     return (i == LoginSessions_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1705 uint64_t WebUsers::searchHashesDatabaseForHash(
const std::string& hash)
 
 1709     for(; i < Hashes_.size(); ++i)
 
 1710         if(Hashes_[i].hash_ == hash)
 
 1715     if(i < Hashes_.size())  
 
 1716         Hashes_[i].accessTime_ =
 
 1717             ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) &
 
 1718              0x0FFFFFFFFFE000000);
 
 1723     return (i == Hashes_.size()) ? NOT_FOUND_IN_DATABASE : i;
 
 1730 bool WebUsers::addToHashesDatabase(
const std::string& hash)
 
 1732     if(searchHashesDatabaseForHash(hash) != NOT_FOUND_IN_DATABASE)
 
 1734         __COUT__ << 
"Hash collision: " << hash << __E__;
 
 1737     Hashes_.push_back(Hash());
 
 1738     Hashes_.back().hash_ = hash;
 
 1739     Hashes_.back().accessTime_ =
 
 1740         ((time(0) + (rand() % 2 ? 1 : -1) * (rand() % 30 * 24 * 60 * 60)) &
 
 1741          0x0FFFFFFFFFE000000);
 
 1744     return saveDatabaseToFile(DB_HASHES);
 
 1749 std::string WebUsers::genCookieCode()
 
 1752     std::string cc = 
"";
 
 1753     for(uint32_t i = 0; i < COOKIE_CODE_LENGTH / 2; ++i)
 
 1755         intToHexStr(rand(), hexStr);
 
 1765 std::string WebUsers::createNewActiveSession(uint64_t           uid,
 
 1766                                              const std::string& ip,
 
 1770     ActiveSessions_.push_back(ActiveSession());
 
 1771     ActiveSessions_.back().cookieCode_ = genCookieCode();
 
 1772     ActiveSessions_.back().ip_         = ip;
 
 1773     ActiveSessions_.back().userId_     = uid;
 
 1774     ActiveSessions_.back().startTime_  = time(0);
 
 1777         ActiveSessions_.back().sessionIndex_ = asIndex;
 
 1782         for(uint64_t j = 0; j < ActiveSessions_.size(); ++j)
 
 1783             if(ActiveSessions_[j].userId_ == uid &&
 
 1784                max < ActiveSessions_[j].sessionIndex_)  
 
 1785                 max = ActiveSessions_[j].sessionIndex_;
 
 1787         ActiveSessions_.back().sessionIndex_ = (max ? max + 1 : 1);  
 
 1790     return ActiveSessions_.back().cookieCode_;
 
 1817 std::string WebUsers::refreshCookieCode(
unsigned int i, 
bool enableRefresh)
 
 1820     for(uint64_t j = ActiveSessions_.size() - 1; j != (uint64_t)-1;
 
 1822         if(ActiveSessions_[j].userId_ == ActiveSessions_[i].userId_ &&
 
 1823            ActiveSessions_[j].sessionIndex_ ==
 
 1824                ActiveSessions_[i].sessionIndex_)  
 
 1829             if(enableRefresh && (time(0) - ActiveSessions_[j].startTime_ >
 
 1830                                  ACTIVE_SESSION_EXPIRATION_TIME / 2))
 
 1834                 ActiveSessions_[j].startTime_ =
 
 1835                     time(0) - ACTIVE_SESSION_EXPIRATION_TIME +
 
 1836                     ACTIVE_SESSION_COOKIE_OVERLAP_TIME;  
 
 1842                 return createNewActiveSession(ActiveSessions_[i].userId_,
 
 1843                                               ActiveSessions_[i].ip_,
 
 1844                                               ActiveSessions_[i].sessionIndex_);
 
 1847             return ActiveSessions_[j].cookieCode_;  
 
 1859                                               std::string&       cookieCode,
 
 1860                                               std::string&       username)
 
 1862     if(!CareAboutCookieCodes_)
 
 1869     if(!ActiveSessions_.size())
 
 1870         return NOT_FOUND_IN_DATABASE;  
 
 1875     if((i = searchLoginSessionDatabaseForUUID(uuid)) == NOT_FOUND_IN_DATABASE)
 
 1877         __COUT__ << 
"uuid not found: " << uuid << __E__;
 
 1878         return NOT_FOUND_IN_DATABASE;
 
 1882         dejumble(username, LoginSessions_[i].id_);  
 
 1885     if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
 
 1887         __COUT__ << 
"Cookie code not found" << __E__;
 
 1888         return NOT_FOUND_IN_DATABASE;
 
 1892     if((j = searchUsersDatabaseForUserId(ActiveSessions_[i].userId_)) ==
 
 1893        NOT_FOUND_IN_DATABASE)
 
 1895         __COUT__ << 
"User ID not found" << __E__;
 
 1896         return NOT_FOUND_IN_DATABASE;
 
 1900     if(Users_[j].username_ != username)
 
 1902         __COUT__ << 
"cookieCode: " << cookieCode << 
" was.." << __E__;
 
 1903         __COUT__ << 
"username: " << username << 
" is not found" << __E__;
 
 1904         return NOT_FOUND_IN_DATABASE;
 
 1907     username   = Users_[j].displayName_;  
 
 1908     cookieCode = refreshCookieCode(i);    
 
 1909     return Users_[j].userId_;             
 
 1918     std::vector<uint64_t> uniqueAsi;  
 
 1921     for(i = 0; i < ActiveSessions_.size(); ++i)
 
 1922         if(ActiveSessions_[i].userId_ == uid)  
 
 1927             for(j = 0; j < uniqueAsi.size(); ++j)
 
 1928                 if(uniqueAsi[j] == ActiveSessions_[i].sessionIndex_)
 
 1935                 uniqueAsi.push_back(ActiveSessions_[i].sessionIndex_);
 
 1938     __COUT__ << 
"Found " << uniqueAsi.size() << 
" active sessions for uid " << uid
 
 1941     return uniqueAsi.size();
 
 1957     if(time(0) > ipSecurityLastLoadTime_ +
 
 1960         ipSecurityLastLoadTime_ = time(0);
 
 1961         loadIPAddressSecurity();
 
 1964     for(
const auto& acceptIp : ipAccessAccept_)
 
 1967             __COUTV__(acceptIp);
 
 1970     for(
const auto& rejectIp : ipAccessReject_)
 
 1973             __COUTV__(rejectIp);
 
 1976     for(
const auto& blacklistIp : ipAccessBlacklist_)
 
 1979             __COUTV__(blacklistIp);
 
 1989 void WebUsers::incrementIpBlacklistCount(
const std::string& ip)
 
 1991     if(ipAccessBlacklist_.find(ip) != ipAccessBlacklist_.end())
 
 1995     auto it = ipBlacklistCounts_.find(ip);
 
 1996     if(it == ipBlacklistCounts_.end())
 
 1998         __COUT__ << 
"First error for ip '" << ip << 
"'" << __E__;
 
 1999         ipBlacklistCounts_[ip] = 1;
 
 2005         if(it->second >= IP_BLACKLIST_COUNT_THRESHOLD)
 
 2007             __COUT_WARN__ << 
"Adding IP '" << ip << 
"' to blacklist!" << __E__;
 
 2009             ipAccessBlacklist_.emplace(ip);
 
 2010             __COUTV__(ipAccessBlacklist_.size());
 
 2013             FILE* fp = fopen((IP_BLACKLIST_FILE).c_str(), 
"a");
 
 2016                 __COUT_ERR__ << 
"IP black list file '" << IP_BLACKLIST_FILE
 
 2017                              << 
"' could not be opened." << __E__;
 
 2020             fprintf(fp, 
"%s\n", ip.c_str());
 
 2031     if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
 
 2033     return Users_[i].displayName_;
 
 2041     if((i = searchUsersDatabaseForUserId(uid)) == NOT_FOUND_IN_DATABASE)
 
 2043     return Users_[i].username_;
 
 2058                                     bool               logoutOtherUserSessions,
 
 2060                                     const std::string& ip)
 
 2065     if((i = searchActiveSessionDatabaseForCookie(cookieCode)) == NOT_FOUND_IN_DATABASE)
 
 2067         __COUT__ << 
"Cookie code not found" << __E__;
 
 2069         incrementIpBlacklistCount(ip);  
 
 2071         return NOT_FOUND_IN_DATABASE;
 
 2074         ipBlacklistCounts_[ip] = 0;  
 
 2077     if(ActiveSessions_[i].ip_ != ip)
 
 2079         __COUT__ << 
"IP does not match active session" << __E__;
 
 2080         return NOT_FOUND_IN_DATABASE;
 
 2089     uint64_t asi = ActiveSessions_[i].sessionIndex_;
 
 2090     uint64_t uid = ActiveSessions_[i].userId_;
 
 2093     uint64_t logoutCount = 0;
 
 2096     while(i < ActiveSessions_.size())
 
 2098         if((logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid &&
 
 2099             ActiveSessions_[i].sessionIndex_ != asi) ||
 
 2100            (!logoutOtherUserSessions && ActiveSessions_[i].userId_ == uid &&
 
 2101             ActiveSessions_[i].sessionIndex_ == asi))
 
 2103             __COUT__ << 
"Logging out of active session " << ActiveSessions_[i].userId_
 
 2104                      << 
"-" << ActiveSessions_[i].sessionIndex_ << __E__;
 
 2105             ActiveSessions_.erase(ActiveSessions_.begin() + i);
 
 2112     __COUT__ << 
"Found and removed active session count = " << logoutCount << __E__;
 
 2131     std::string&                                                      cookieCode,
 
 2132     std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>* userPermissions,
 
 2134     const std::string&                                                ip,
 
 2137     std::string*                                                      userWithLock,
 
 2138     uint64_t*                                                         userSessionIndex)
 
 2145         __COUT_ERR__ << 
"User IP rejected." << __E__;
 
 2146         cookieCode = REQ_NO_LOGIN_RESPONSE;
 
 2152     uint64_t i, j, userId = NOT_FOUND_IN_DATABASE, userSession = NOT_FOUND_IN_DATABASE;
 
 2154     __COUTTV__(CareAboutCookieCodes_);
 
 2155     __COUTT__ << 
"refresh=" << refresh << 
", doNotGoRemote=" << doNotGoRemote << __E__;
 
 2156     __COUTVS__(2, cookieCode);
 
 2160     __COUTTV__(localEnableRemoteLogin);
 
 2165         if(localEnableRemoteLogin &&
 
 2166            time(0) > remoteLoginVerificationEnabledBlackoutTime_ &&
 
 2167            (userId = checkRemoteLoginVerification(
 
 2168                 cookieCode, refresh, doNotGoRemote, ip)) != NOT_FOUND_IN_DATABASE)
 
 2171             __COUTT__ << 
"Remote login session verified." << __E__;
 
 2172             userSession = RemoteSessions_.at(cookieCode).sessionIndex_;
 
 2177         std::string err = 
"";
 
 2182         catch(
const std::exception& e)
 
 2187         __COUT_WARN__ << 
"Ignoring exception during remote login verification. " << err
 
 2191         if(!CareAboutCookieCodes_ && localEnableRemoteLogin &&
 
 2192            remoteLoginVerificationEnabledBlackoutTime_ == 0)
 
 2195             localEnableRemoteLogin                      = 
false;  
 
 2196             remoteLoginVerificationEnabledBlackoutTime_ = time(0) + 10;
 
 2197             __COUT_INFO__ << 
"Disabled remote login until " 
 2199                                  remoteLoginVerificationEnabledBlackoutTime_)
 
 2203     __COUTTV__(localEnableRemoteLogin);
 
 2205     if(localEnableRemoteLogin && userId == NOT_FOUND_IN_DATABASE)
 
 2206         __COUTT__ << 
"Remote login verification failed." << __E__;
 
 2208     if(!CareAboutCookieCodes_ &&
 
 2209        userId == NOT_FOUND_IN_DATABASE)  
 
 2213                 std::map<std::string , WebUsers::permissionLevel_t>(
 
 2218             *userWithLock = usersUsernameWithLock_;
 
 2219         if(userSessionIndex)
 
 2220             *userSessionIndex = 0;
 
 2222         if(cookieCode.size() != COOKIE_CODE_LENGTH)
 
 2223             cookieCode = genCookieCode();  
 
 2225         if(localEnableRemoteLogin)  
 
 2227             cookieCode = WebUsers::
 
 2235     if(userId == NOT_FOUND_IN_DATABASE)  
 
 2238         if((i = searchActiveSessionDatabaseForCookie(cookieCode)) ==
 
 2239            NOT_FOUND_IN_DATABASE)
 
 2241             __COUT_ERR__ << 
"Cookie code not found" << __E__;
 
 2242             cookieCode = REQ_NO_LOGIN_RESPONSE;
 
 2244             incrementIpBlacklistCount(ip);  
 
 2249             ipBlacklistCounts_[ip] = 0;  
 
 2252         if(ip != 
"0" && ActiveSessions_[i].ip_ != ip)
 
 2254             __COUTV__(ActiveSessions_[i].ip_);
 
 2256             __COUT_ERR__ << 
"IP does not match active session." << __E__;
 
 2257             cookieCode = REQ_NO_LOGIN_RESPONSE;
 
 2261         userId      = ActiveSessions_[i].userId_;
 
 2262         userSession = ActiveSessions_[i].sessionIndex_;
 
 2263         cookieCode  = refreshCookieCode(i, refresh);  
 
 2264         __COUTT__ << 
"Login session verified." << __E__;
 
 2270     if((j = searchUsersDatabaseForUserId(userId)) == NOT_FOUND_IN_DATABASE)
 
 2272         __COUT_ERR__ << 
"After login verification, User ID not found! Notify admins." 
 2274         cookieCode = REQ_NO_LOGIN_RESPONSE;
 
 2278     std::map<std::string , WebUsers::permissionLevel_t> tmpPerm =
 
 2281     if(isInactiveForGroup(tmpPerm))  
 
 2283         __COUTT__ << 
"Inactive user identified." << __E__;
 
 2284         cookieCode = REQ_NO_PERMISSION_RESPONSE;
 
 2290         *userPermissions = tmpPerm;
 
 2294         *userWithLock = usersUsernameWithLock_;
 
 2295     if(userSessionIndex)
 
 2296         *userSessionIndex = userSession;
 
 2313     if(loggedOutUsernames)  
 
 2315         for(i = 0; i < UsersLoggedOutUsernames_.size(); ++i)
 
 2316             loggedOutUsernames->push_back(UsersLoggedOutUsernames_[i]);
 
 2317         UsersLoggedOutUsernames_.clear();
 
 2321     for(i = 0; i < LoginSessions_.size(); ++i)
 
 2322         if(LoginSessions_[i].startTime_ + LOGIN_SESSION_EXPIRATION_TIME <
 
 2324            LoginSessions_[i].loginAttempts_ > LOGIN_SESSION_ATTEMPTS_MAX)
 
 2326             __COUT__ << 
"Found expired login sessions: #" << (i + 1) << 
" of " 
 2327                      << LoginSessions_.size() << __E__;
 
 2331             LoginSessions_.erase(LoginSessions_.begin() + i);
 
 2344     for(i = 0; i < ActiveSessions_.size(); ++i)
 
 2345         if(ActiveSessions_[i].startTime_ + ACTIVE_SESSION_EXPIRATION_TIME <=
 
 2355             __COUT__ << 
"Found expired active sessions: #" << (i + 1) << 
" of " 
 2356                      << ActiveSessions_.size() << __E__;
 
 2357             __COUTTV__(ActiveSessions_[i].cookieCode_);
 
 2359             tmpUid = ActiveSessions_[i].userId_;
 
 2360             ActiveSessions_.erase(ActiveSessions_.begin() + i);
 
 2365                 if(loggedOutUsernames)  
 
 2366                     loggedOutUsernames->push_back(
 
 2367                         Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
 
 2369                     UsersLoggedOutUsernames_.push_back(
 
 2370                         Users_[searchUsersDatabaseForUserId(tmpUid)].username_);
 
 2391     if(CareAboutCookieCodes_ && usersUsernameWithLock_ != 
"" &&
 
 2399         usersUsernameWithLock_ = 
"";
 
 2409     std::vector<std::string> toErase;
 
 2410     for(
const auto& remoteSession : RemoteSessions_)
 
 2411         if(remoteSession.second.startTime_ + ACTIVE_SESSION_EXPIRATION_TIME / 4 <=
 
 2414             __COUT__ << 
"Found expired remote active sessions: #" << remoteSession.first
 
 2415                      << 
" in " << RemoteSessions_.size() << __E__;
 
 2416             toErase.push_back(remoteSession.first);  
 
 2418     for(
const auto& eraseId : toErase)
 
 2419         RemoteSessions_.erase(eraseId);
 
 2430                                             const std::string& ip)
 
 2436     for(; i < LoginSessions_.size(); ++i)
 
 2437         if(LoginSessions_[i].uuid_ == UUID)
 
 2440     if(i != LoginSessions_.size())
 
 2442         __COUT_ERR__ << 
"UUID: " << UUID << 
" is not unique" << __E__;
 
 2448     LoginSessions_.back().uuid_ = UUID;
 
 2452     std::string sid = 
"";
 
 2453     for(i = 0; i < SESSION_ID_LENGTH / 2; ++i)
 
 2455         intToHexStr(rand(), hexStr);
 
 2458     LoginSessions_.back().id_            = sid;
 
 2459     LoginSessions_.back().ip_            = ip;
 
 2460     LoginSessions_.back().startTime_     = time(0);
 
 2461     LoginSessions_.back().loginAttempts_ = 0;
 
 2471 std::string WebUsers::sha512(
const std::string& user,
 
 2472                              const std::string& password,
 
 2475     SHA512_CTX sha512_context;
 
 2480         SHA512_Init(&sha512_context);
 
 2482         for(
unsigned int i = 0; i < 8; ++i)
 
 2483             sha512_context.h[i] += rand();
 
 2485         for(
unsigned int i = 0; i < 
sizeof(SHA512_CTX); ++i)
 
 2487             intToHexStr((uint8_t)(((uint8_t*)(&sha512_context))[i]), hexStr);
 
 2489             salt.append(hexStr);
 
 2497         for(
unsigned int i = 0; i < 
sizeof(SHA512_CTX); ++i)
 
 2498             ((uint8_t*)(&sha512_context))[i] = hexByteStrToInt(&(salt.c_str()[i * 2]));
 
 2501     std::string strToHash = salt + user + password;
 
 2504     unsigned char hash[SHA512_DIGEST_LENGTH];
 
 2506     char retHash[SHA512_DIGEST_LENGTH * 2 + 1];
 
 2510     SHA512_Update(&sha512_context, strToHash.c_str(), strToHash.length());
 
 2512     SHA512_Final(hash, &sha512_context);
 
 2516     for(i = 0; i < SHA512_DIGEST_LENGTH; i++)
 
 2517         sprintf(retHash + (i * 2), 
"%02x", hash[i]);
 
 2520     retHash[SHA512_DIGEST_LENGTH * 2] = 
'\0';
 
 2531 std::string WebUsers::dejumble(
const std::string& u, 
const std::string& s)
 
 2533     if(s.length() != SESSION_ID_LENGTH)
 
 2536     const int ss  = s.length() / 2;
 
 2537     int       p   = hexByteStrToInt(&(s.c_str()[0])) % ss;
 
 2538     int       n   = hexByteStrToInt(&(s.c_str()[p * 2])) % ss;
 
 2539     int       len = (hexByteStrToInt(&(u.c_str()[p * 2])) - p - n + ss * 3) % ss;
 
 2541     std::vector<bool> x(ss);
 
 2542     for(
int i = 0; i < ss; ++i)
 
 2546     int c = hexByteStrToInt(&(u.c_str()[p * 2]));
 
 2548     std::string user = 
"";
 
 2550     for(
int l = 0; l < len; ++l)
 
 2552         p = (p + hexByteStrToInt(&(s.c_str()[p * 2]))) % ss;
 
 2556         n    = hexByteStrToInt(&(s.c_str()[p * 2]));
 
 2557         user.append(1, (hexByteStrToInt(&(u.c_str()[p * 2])) - c - n + ss * 4) % ss);
 
 2558         c = hexByteStrToInt(&(u.c_str()[p * 2]));
 
 2567 std::map<std::string , WebUsers::permissionLevel_t>
 
 2570     uint64_t userIndex = searchUsersDatabaseForUserId(uid);
 
 2571     if(userIndex < Users_.size())
 
 2572         return Users_[userIndex].permissions_;
 
 2575     std::map<std::string , WebUsers::permissionLevel_t> retErrorMap;
 
 2576     retErrorMap[WebUsers::DEFAULT_USER_GROUP] = WebUsers::PERMISSION_LEVEL_INACTIVE;
 
 2613 WebUsers::permissionLevel_t WebUsers::getPermissionLevelForGroup(
 
 2614     const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
 
 2615     const std::string&                                                      groupName)
 
 2617     auto it = permissionMap.find(groupName);
 
 2618     if(it == permissionMap.end())
 
 2620         __COUT__ << 
"Group name '" << groupName
 
 2621                  << 
"' not found - assuming inactive user in this group." << __E__;
 
 2622         return WebUsers::PERMISSION_LEVEL_INACTIVE;
 
 2628 bool WebUsers::isInactiveForGroup(
 
 2629     const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
 
 2630     const std::string&                                                      groupName)
 
 2632     return getPermissionLevelForGroup(permissionMap, groupName) ==
 
 2633            WebUsers::PERMISSION_LEVEL_INACTIVE;
 
 2637 bool WebUsers::isAdminForGroup(
 
 2638     const std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>& permissionMap,
 
 2639     const std::string&                                                      groupName)
 
 2641     return getPermissionLevelForGroup(permissionMap, groupName) ==
 
 2648 std::string WebUsers::getTooltipFilename(
const std::string& username,
 
 2649                                          const std::string& srcFile,
 
 2650                                          const std::string& srcFunc,
 
 2651                                          const std::string& srcId)
 
 2653     std::string filename = (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH + 
"/";
 
 2657     mkdir(((std::string)WEB_LOGIN_DB_PATH).c_str(), 0755);
 
 2658     mkdir(((std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH).c_str(), 0755);
 
 2659     mkdir(filename.c_str(), 0755);
 
 2661     for(
const char& c : username)
 
 2663             (c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9'))
 
 2668     mkdir(filename.c_str(), 0755);
 
 2670     for(
const char& c : srcFile)
 
 2672             (c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9'))
 
 2675     for(
const char& c : srcFunc)
 
 2677             (c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9'))
 
 2680     for(
const char& c : srcId)
 
 2682             (c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9'))
 
 2689 std::string ots::WebUsers::getUserEmailFromFingerprint(
const std::string& fingerprint)
 
 2691     __COUT__ << 
"Checking if user fingerprint " << fingerprint << 
" is in memory database" 
 2693     if(certFingerprints_.count(fingerprint))
 
 2695         return certFingerprints_[fingerprint];
 
 2698     __COUT__ << 
"Going to read credential database " << WEB_LOGIN_CERTDATA_PATH << __E__;
 
 2699     std::ifstream f(WEB_LOGIN_CERTDATA_PATH);
 
 2707         if(fp != 
"NOKEY" && fp != 
"")
 
 2709             __COUT__ << 
"Adding user " << email << 
" to list with fingerprint " << fp
 
 2711             certFingerprints_[fp] = email;
 
 2717         remove(WEB_LOGIN_CERTDATA_PATH.c_str());
 
 2720     __COUT__ << 
"Checking again if fingerprint is in memory database" << __E__;
 
 2721     if(certFingerprints_.count(fingerprint))
 
 2723         return certFingerprints_[fingerprint];
 
 2726     __COUT__ << 
"Could not match fingerprint, returning null email" << __E__;
 
 2735                                               const std::string& srcFile,
 
 2736                                               const std::string& srcFunc,
 
 2737                                               const std::string& srcId,
 
 2739                                               bool               temporarySilence)
 
 2741     std::string filename;
 
 2742     bool        isForAll = (srcFile == 
"ALL" && srcFunc == 
"ALL" && srcId == 
"ALL");
 
 2746         __COUT__ << 
"Disabling ALL tooltips for user '" << username << 
"' is now set to " 
 2747                  << doNeverShow << __E__;
 
 2748         filename = getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME, 
"", 
"");
 
 2752         filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
 
 2753         __COUT__ << 
"Setting tooltip never show for user '" << username << 
"' to " 
 2754                  << doNeverShow << 
" (temporarySilence=" << temporarySilence << 
")" 
 2758     FILE* fp = fopen(filename.c_str(), 
"w");
 
 2761         if(temporarySilence)
 
 2764                     time(0) + 7  * 24  * 60 * 60);  
 
 2765         else if(!isForAll && doNeverShow && username == WebUsers::DEFAULT_ADMIN_USERNAME)
 
 2768             fprintf(fp, 
"%ld", time(0) + 30  * 24  * 60 * 60);
 
 2770             __COUT__ << 
"User '" << username
 
 2771                      << 
"' may be a shared account, so max silence duration for tooltips " 
 2772                         "is 30 days. Silencing now." 
 2776             fputc(doNeverShow ? 
'1' : 
'0', fp);
 
 2780         __COUT_ERR__ << 
"Big problem with tooltips! File not accessible: " << filename
 
 2793                                        const std::string& srcFile,
 
 2794                                        const std::string& srcFunc,
 
 2795                                        const std::string& srcId)
 
 2797     if(srcId == 
"ALWAYS")
 
 2800         xmldoc->addTextElementToData(
"ShowTooltip", 
"1");
 
 2811     std::string silencefilename =
 
 2812         getTooltipFilename(username, SILENCE_ALL_TOOLTIPS_FILENAME, 
"", 
"");
 
 2814     FILE* silencefp = fopen(silencefilename.c_str(), 
"r");
 
 2815     if(silencefp != NULL)
 
 2819         fgets(line, 100, silencefp);
 
 2820         sscanf(line, 
"%ld", &val);
 
 2824             xmldoc->addTextElementToData(
"ShowTooltip", 
"0");
 
 2830     std::string filename = getTooltipFilename(username, srcFile, srcFunc, srcId);
 
 2831     FILE*       fp       = fopen(filename.c_str(), 
"r");
 
 2836         fgets(line, 100, fp);
 
 2837         sscanf(line, 
"%ld", &val);
 
 2840         __COUT__ << 
"tooltip value read = " << val << 
" vs time(0)=" << time(0) << __E__;
 
 2844         xmldoc->addTextElementToData(
"ShowTooltip",
 
 2845                                      val == 1 ? 
"0" : (time(0) > val ? 
"1" : 
"0"));
 
 2849         xmldoc->addTextElementToData(
"ShowTooltip", 
"1");
 
 2859         (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH + 
"/" + userNeedle)
 
 2861     __COUT__ << 
"Successfully reset Tooltips for user " << userNeedle << __E__;
 
 2869     std::string silencefilename = getTooltipFilename(
 
 2870         username, SILENCE_ALL_TOOLTIPS_FILENAME, 
"", 
"");  
 
 2872     __COUTV__(silencefilename);
 
 2873     FILE* silencefp = fopen(silencefilename.c_str(), 
"w");
 
 2874     if(silencefp != NULL)
 
 2876         fputs(
"1", silencefp);
 
 2904                                      bool             includeAccounts)
 
 2906     std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
 
 2910     if(isInactiveForGroup(permissionMap))
 
 2913     uint64_t userIndex = searchUsersDatabaseForUserId(uid);
 
 2914     __COUT__ << 
"Gettings settings for user: " << Users_[userIndex].username_ << __E__;
 
 2916     std::string fn = (std::string)WEB_LOGIN_DB_PATH +
 
 2917                      (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
 
 2918                      "." + (std::string)USERS_PREFERENCES_FILETYPE;
 
 2922     __COUT__ << 
"Preferences file: " << fn << __E__;
 
 2926         __COUT__ << 
"Preferences are defaults." << __E__;
 
 2928         xmldoc->addTextElementToData(PREF_XML_BGCOLOR_FIELD, PREF_XML_BGCOLOR_DEFAULT);
 
 2929         xmldoc->addTextElementToData(PREF_XML_DBCOLOR_FIELD, PREF_XML_DBCOLOR_DEFAULT);
 
 2930         xmldoc->addTextElementToData(PREF_XML_WINCOLOR_FIELD, PREF_XML_WINCOLOR_DEFAULT);
 
 2931         xmldoc->addTextElementToData(PREF_XML_LAYOUT_FIELD, PREF_XML_LAYOUT_DEFAULT);
 
 2935         __COUT__ << 
"Saved Preferences found." << __E__;
 
 2940     if(includeAccounts && isAdminForGroup(permissionMap))
 
 2942         __COUT__ << 
"Admin on our hands" << __E__;
 
 2944         xmldoc->addTextElementToData(PREF_XML_ACCOUNTS_FIELD, 
"");
 
 2946         if(Users_.size() == 0)
 
 2948             __COUT__ << 
"Missing users? Attempting to load database" << __E__;
 
 2953         for(uint64_t i = 0; i < Users_.size(); ++i)
 
 2956                 "username", Users_[i].username_, PREF_XML_ACCOUNTS_FIELD);
 
 2958                 "display_name", Users_[i].displayName_, PREF_XML_ACCOUNTS_FIELD);
 
 2960             if(Users_[i].email_.size() > i)
 
 2963                     "useremail", Users_[i].email_, PREF_XML_ACCOUNTS_FIELD);
 
 2973                 PREF_XML_ACCOUNTS_FIELD);
 
 2976                 "nac", Users_[i].getNewAccountCode().c_str(), PREF_XML_ACCOUNTS_FIELD);
 
 2981     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
 
 2982          (std::string)SYSTEM_PREFERENCES_PREFIX + 
"." +
 
 2983          (std::string)USERS_PREFERENCES_FILETYPE;
 
 2986         __COUT__ << 
"System Preferences are defaults." << __E__;
 
 2988         xmldoc->addTextElementToData(PREF_XML_SYSLAYOUT_FIELD,
 
 2989                                      PREF_XML_SYSLAYOUT_DEFAULT);
 
 2993         __COUT__ << 
"Saved System Preferences found." << __E__;
 
 3000     xmldoc->addTextElementToData(PREF_XML_PERMISSIONS_FIELD,
 
 3004     xmldoc->addTextElementToData(PREF_XML_USERLOCK_FIELD, usersUsernameWithLock_);
 
 3007     xmldoc->addTextElementToData(PREF_XML_USERNAME_FIELD, 
getUsersUsername(uid));
 
 3013         xmldoc->addTextElementToData(
"ots_remote_address",
 
 3014                                      remoteLoginVerificationIP_ + 
":" +
 
 3023                                     const std::string& preferenceName,
 
 3024                                     const std::string& preferenceValue)
 
 3026     uint64_t userIndex = searchUsersDatabaseForUserId(uid);
 
 3031     std::string safePreferenceName = 
"";
 
 3032     for(
const auto& c : preferenceName)
 
 3033         if((c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9') ||
 
 3034            (c >= 
'-' || c <= 
'_'))
 
 3035             safePreferenceName += c;
 
 3037     std::string dir = (std::string)WEB_LOGIN_DB_PATH +
 
 3038                       (std::string)USERS_PREFERENCES_PATH + 
"generic_" +
 
 3039                       safePreferenceName + 
"/";
 
 3042     mkdir(dir.c_str(), 0755);
 
 3044     std::string fn = Users_[userIndex].username_ + 
"_" + safePreferenceName + 
"." +
 
 3045                      (std::string)USERS_PREFERENCES_FILETYPE;
 
 3047     __COUT__ << 
"Preferences file: " << (dir + fn) << __E__;
 
 3049     FILE* fp = fopen((dir + fn).c_str(), 
"w");
 
 3052         fprintf(fp, 
"%s", preferenceValue.c_str());
 
 3056         __COUT_ERR__ << 
"Preferences file could not be opened for writing!" << __E__;
 
 3064                                            const std::string& preferenceName,
 
 3067     uint64_t userIndex = searchUsersDatabaseForUserId(uid);
 
 3072     std::string safePreferenceName = 
"";
 
 3073     for(
const auto& c : preferenceName)
 
 3074         if((c >= 
'a' && c <= 
'z') || (c >= 
'A' && c <= 
'Z') || (c >= 
'0' && c <= 
'9') ||
 
 3075            (c >= 
'-' || c <= 
'_'))
 
 3076             safePreferenceName += c;
 
 3078     std::string dir = (std::string)WEB_LOGIN_DB_PATH +
 
 3079                       (std::string)USERS_PREFERENCES_PATH + 
"generic_" +
 
 3080                       safePreferenceName + 
"/";
 
 3082     std::string fn = Users_[userIndex].username_ + 
"_" + safePreferenceName + 
"." +
 
 3083                      (std::string)USERS_PREFERENCES_FILETYPE;
 
 3085     __COUT__ << 
"Preferences file: " << (dir + fn) << __E__;
 
 3088     FILE* fp = fopen((dir + fn).c_str(), 
"r");
 
 3091         fseek(fp, 0, SEEK_END);
 
 3092         const long size = ftell(fp);
 
 3093         char*      line = 
new char 
 3097         fread(line, 1, size, fp);
 
 3100         std::string retVal(line, size);
 
 3103         __COUT__ << 
"Read value (sz = " << retVal.size() << 
") " << retVal << __E__;
 
 3105             xmldoc->addTextElementToData(safePreferenceName, retVal);
 
 3109         __COUT__ << 
"Using default value." << __E__;
 
 3113         xmldoc->addTextElementToData(safePreferenceName, 
"");
 
 3120                                      const std::string& bgcolor,
 
 3121                                      const std::string& dbcolor,
 
 3122                                      const std::string& wincolor,
 
 3123                                      const std::string& layout,
 
 3124                                      const std::string& syslayout)
 
 3126     std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
 
 3128     if(isInactiveForGroup(permissionMap))
 
 3131     uint64_t userIndex = searchUsersDatabaseForUserId(uid);
 
 3132     __COUT__ << 
"Changing settings for user: " << Users_[userIndex].username_ << __E__;
 
 3134     std::string fn = (std::string)WEB_LOGIN_DB_PATH +
 
 3135                      (std::string)USERS_PREFERENCES_PATH + Users_[userIndex].username_ +
 
 3136                      "." + (std::string)USERS_PREFERENCES_FILETYPE;
 
 3138     __COUT__ << 
"Preferences file: " << fn << __E__;
 
 3141     prefXml.addTextElementToData(PREF_XML_BGCOLOR_FIELD, bgcolor);
 
 3142     prefXml.addTextElementToData(PREF_XML_DBCOLOR_FIELD, dbcolor);
 
 3143     prefXml.addTextElementToData(PREF_XML_WINCOLOR_FIELD, wincolor);
 
 3144     prefXml.addTextElementToData(PREF_XML_LAYOUT_FIELD, layout);
 
 3149     if(!isAdminForGroup(permissionMap))
 
 3153     fn = (std::string)WEB_LOGIN_DB_PATH + (std::string)USERS_PREFERENCES_PATH +
 
 3154          (std::string)SYSTEM_PREFERENCES_PREFIX + 
"." +
 
 3155          (std::string)USERS_PREFERENCES_FILETYPE;
 
 3158     sysPrefXml.addTextElementToData(PREF_XML_SYSLAYOUT_FIELD, syslayout);
 
 3170     std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
 
 3176     __COUTV__(actingUser);
 
 3178     __COUTV__(usersUsernameWithLock_);
 
 3180     __COUTV__(username);
 
 3181     __COUTV__(isUserActive);
 
 3183     if(lock && (isUserActive || !CareAboutCookieCodes_))  
 
 3185         if(!CareAboutCookieCodes_ && !isUserActive &&
 
 3186            username != DEFAULT_ADMIN_USERNAME)  
 
 3189                 << 
"User '" << actingUser
 
 3190                 << 
"' tried to lock for a user other than admin in wiz mode. Not allowed." 
 3194         else if(!isAdminForGroup(permissionMap) &&
 
 3195                 actingUser != username)  
 
 3197             __COUT_ERR__ << 
"A non-admin user '" << actingUser
 
 3198                          << 
"' tried to lock for a user other than self. Not allowed." 
 3202         usersUsernameWithLock_ = username;
 
 3204     else if(!lock && usersUsernameWithLock_ == username)  
 
 3205         usersUsernameWithLock_ = 
"";
 
 3209             __COUT_ERR__ << 
"User '" << username << 
"' is inactive." << __E__;
 
 3210         __COUT_ERR__ << 
"Failed to lock for user '" << username << 
".'" << __E__;
 
 3214     __COUT_INFO__ << 
"User '" << username << 
"' has locked out the system!" << __E__;
 
 3218         std::string securityFileName = USER_WITH_LOCK_FILE;
 
 3219         FILE*       fp               = fopen(securityFileName.c_str(), 
"w");
 
 3222             __COUT_INFO__ << 
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE
 
 3223                           << 
" not found. Ignoring." << __E__;
 
 3227             fprintf(fp, 
"%s", usersUsernameWithLock_.c_str());
 
 3238                                      const std::string& username,
 
 3239                                      const std::string& displayname,
 
 3240                                      const std::string& email,
 
 3241                                      const std::string& permissions)
 
 3243     std::map<std::string , WebUsers::permissionLevel_t> permissionMap =
 
 3245     if(!isAdminForGroup(permissionMap))
 
 3248         __SS__ << 
"Only admins can modify user settings." << __E__;
 
 3252     uint64_t i    = searchUsersDatabaseForUserId(actingUid);
 
 3253     uint64_t modi = searchUsersDatabaseForUsername(username);
 
 3258             __COUT_INFO__ << 
"Admin password reset." << __E__;
 
 3259             Users_[modi].setModifier(Users_[i].username_);
 
 3260             Users_[modi].salt_              = 
"";
 
 3261             Users_[modi].loginFailureCount_ = 0;
 
 3262             saveDatabaseToFile(DB_USERS);
 
 3265         __SS__ << 
"Cannot modify first user" << __E__;
 
 3269     if(username.length() < USERNAME_LENGTH)
 
 3271         __SS__ << 
"Invalid Username, must be length " << USERNAME_LENGTH << __E__;
 
 3274     if(displayname.length() < DISPLAY_NAME_LENGTH)
 
 3276         __SS__ << 
"Invalid Display Name; must be length " << DISPLAY_NAME_LENGTH << __E__;
 
 3280     __COUT__ << 
"Input Permissions: " << permissions << __E__;
 
 3281     std::map<std::string , WebUsers::permissionLevel_t> newPermissionsMap;
 
 3285     case MOD_TYPE_UPDATE:
 
 3287         __COUT__ << 
"MOD_TYPE_UPDATE " << username << 
" := " << permissions << __E__;
 
 3289         if(modi == NOT_FOUND_IN_DATABASE)
 
 3291             __SS__ << 
"User not found!? Should not happen." << __E__;
 
 3297             for(uint64_t i = 0; i < Users_.size(); ++i)
 
 3300                 else if(Users_[i].displayName_ == displayname)
 
 3302                     __SS__ << 
"Display Name '" << displayname
 
 3303                            << 
"' already exists! Please choose a unique display name." 
 3309         Users_[modi].displayName_ = displayname;
 
 3310         Users_[modi].email_       = email;
 
 3314             bool wasInactive = isInactiveForGroup(Users_[modi].permissions_);
 
 3317             if(newPermissionsMap.size() == 0)  
 
 3318                 Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] =
 
 3319                     std::atoi(permissions.c_str());
 
 3320             else if(newPermissionsMap.size() == 1 &&
 
 3321                     newPermissionsMap.find(WebUsers::DEFAULT_USER_GROUP) ==
 
 3322                         newPermissionsMap.end())
 
 3324                 if(newPermissionsMap.begin()->first == 
"")
 
 3325                     Users_[modi].permissions_[WebUsers::DEFAULT_USER_GROUP] =
 
 3326                         newPermissionsMap.begin()->second;
 
 3329                     newPermissionsMap[WebUsers::DEFAULT_USER_GROUP] =
 
 3330                         newPermissionsMap.begin()->second;
 
 3331                     Users_[modi].permissions_ = newPermissionsMap;
 
 3335                 Users_[modi].permissions_ = newPermissionsMap;
 
 3340                !isInactiveForGroup(Users_[modi].permissions_))  
 
 3342                 __COUT__ << 
"Reactivating " << username << __E__;
 
 3343                 Users_[modi].loginFailureCount_ = 0;
 
 3344                 Users_[modi].salt_              = 
"";
 
 3350             if(i == NOT_FOUND_IN_DATABASE)
 
 3352                 __SS__ << 
"Master User not found!? Should not happen." << __E__;
 
 3355             Users_[modi].setModifier(Users_[i].username_);
 
 3361         __COUT__ << 
"MOD_TYPE_ADD " << username << 
" - " << displayname << __E__;
 
 3366             if(i == NOT_FOUND_IN_DATABASE)
 
 3368                 __SS__ << 
"Master User not found!? Should not happen." << __E__;
 
 3371             Users_.back().setModifier(Users_[i].username_);
 
 3374         if(permissions.size())  
 
 3377                 actingUid, MOD_TYPE_UPDATE, username, displayname, email, permissions);
 
 3381     case MOD_TYPE_DELETE:
 
 3382         __COUT__ << 
"MOD_TYPE_DELETE " << username << 
" - " << displayname << __E__;
 
 3383         deleteAccount(username, displayname);
 
 3386         __SS__ << 
"Undefined command - do nothing " << username << __E__;
 
 3390     saveDatabaseToFile(DB_USERS);
 
 3391     loadSecuritySelection();  
 
 3398     std::set<unsigned int> activeUserIndices;
 
 3399     for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
 
 3400         activeUserIndices.emplace(
 
 3401             searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
 
 3402     return activeUserIndices.size();
 
 3410     std::set<unsigned int> activeUserIndices;
 
 3411     for(uint64_t i = 0; i < ActiveSessions_.size(); ++i)
 
 3412         activeUserIndices.emplace(
 
 3413             searchUsersDatabaseForUserId(ActiveSessions_[i].userId_));
 
 3415     std::string activeUsersString = 
"";
 
 3416     bool        addComma          = 
false;
 
 3417     for(
const auto& i : activeUserIndices)
 
 3419         if(i >= Users_.size())
 
 3423             activeUsersString += 
",";
 
 3427         activeUsersString += Users_[i].displayName_;
 
 3429     if(activeUserIndices.size() == 0 &&
 
 3431            WebUsers::SECURITY_TYPE_NONE)  
 
 3432         activeUsersString += WebUsers::DEFAULT_ADMIN_DISPLAY_NAME;
 
 3434     __COUTVS__(20, activeUsersString);
 
 3435     return activeUsersString;
 
 3443     uint64_t uid = searchUsersDatabaseForUsername(DEFAULT_ADMIN_USERNAME);
 
 3450 void WebUsers::loadUserWithLock()
 
 3452     char username[300] = 
"";  
 
 3454     std::string securityFileName = USER_WITH_LOCK_FILE;
 
 3455     FILE*       fp               = fopen(securityFileName.c_str(), 
"r");
 
 3458         __COUT_INFO__ << 
"USER_WITH_LOCK_FILE " << USER_WITH_LOCK_FILE
 
 3459                       << 
" not found. Defaulting to admin lock." << __E__;
 
 3462         sprintf(username, 
"%s", DEFAULT_ADMIN_USERNAME.c_str());
 
 3466         fgets(username, 300, fp);
 
 3473     __COUT__ << 
"Attempting to load username with lock: " << username << __E__;
 
 3475     if(strlen(username) == 0)
 
 3477         __COUT_INFO__ << 
"Loaded state for user-with-lock is unlocked." << __E__;
 
 3481     uint64_t i = searchUsersDatabaseForUsername(username);
 
 3482     if(i == NOT_FOUND_IN_DATABASE)
 
 3484         __COUT_INFO__ << 
"username " << username << 
" not found in database. Ignoring." 
 3488     __COUT__ << 
"Setting lock" << __E__;
 
 3496                                 const std::string& message)
 
 3505                                 const std::string& subject,
 
 3506                                 const std::string& message,
 
 3509     std::vector<std::string> targetUsers;
 
 3519                                 const std::string&              subject,
 
 3520                                 const std::string&              message,
 
 3523     systemMessageCleanup();
 
 3525     std::string fullMessage = StringMacros::encodeURIComponent(
 
 3526         (subject == 
"" ? 
"" : (subject + 
": ")) + message);
 
 3529     std::cout << __COUT_HDR_FL__ << 
"addSystemMessage() fullMessage: " << fullMessage
 
 3533     std::set<std::string> targetEmails;
 
 3535     for(
const auto& targetUser : targetUsers)
 
 3539         if(targetUser == 
"" || (targetUser != 
"*" && targetUser.size() < 3))
 
 3541             __COUT__ << 
"Illegal username '" << targetUser << 
"'" << __E__;
 
 3544         __COUTV__(targetUser);
 
 3548         if(doEmail && targetUser == 
"*")
 
 3551             for(
const auto& user : Users_)
 
 3553                 if(user.email_.size() > 5 &&  
 
 3554                    user.email_.find(
'@') != std::string::npos &&
 
 3555                    user.email_.find(
'.') != std::string::npos)
 
 3557                     __COUT__ << 
"Adding " << user.displayName_
 
 3558                              << 
" email: " << user.email_ << __E__;
 
 3559                     targetEmails.emplace(user.email_);
 
 3564         else if(targetUser.find(
':') != std::string::npos)
 
 3567             __COUT__ << 
"Treating as group email target: " << targetUser << __E__;
 
 3569             std::map<std::string, WebUsers::permissionLevel_t> targetGroupMap;
 
 3576             if(targetGroupMap.size() == 1)
 
 3581                 for(
const auto& user : Users_)
 
 3583                     WebUsers::permissionLevel_t userLevel =
 
 3585                                                    targetGroupMap.begin()->first);
 
 3589                     __COUTV__((
int)userLevel);
 
 3590                     __COUTV__(targetGroupMap.begin()->first);
 
 3592                     if(userLevel != WebUsers::PERMISSION_LEVEL_INACTIVE &&
 
 3593                        userLevel >= targetGroupMap.begin()->second &&
 
 3594                        user.email_.size() > 5 &&  
 
 3595                        user.email_.find(
'@') != std::string::npos &&
 
 3596                        user.email_.find(
'.') != std::string::npos)
 
 3600                             targetEmails.emplace(user.email_);
 
 3601                             __COUT__ << 
"Adding " << user.displayName_
 
 3602                                      << 
" email: " << user.email_ << __E__;
 
 3604                         addSystemMessageToMap(user.displayName_, fullMessage);
 
 3609                 __COUT__ << 
"target Group Map from '" << targetUser << 
"' is empty." 
 3617         addSystemMessageToMap(targetUser, fullMessage);
 
 3621             for(
const auto& user : Users_)
 
 3623                 if(user.displayName_ == targetUser)
 
 3625                     if(user.email_.size() > 5 &&  
 
 3626                        user.email_.find(
'@') != std::string::npos &&
 
 3627                        user.email_.find(
'.') != std::string::npos)
 
 3629                         targetEmails.emplace(user.email_);
 
 3630                         __COUT__ << 
"Adding " << user.displayName_
 
 3631                                  << 
" email: " << user.email_ << __E__;
 
 3640     __COUTV__(targetEmails.size());
 
 3642     if(doEmail && targetEmails.size())
 
 3646         std::string toList   = 
"";
 
 3647         bool        addComma = 
false;
 
 3648         for(
const auto& email : targetEmails)
 
 3657         std::string filename = (std::string)WEB_LOGIN_DB_PATH +
 
 3658                                (std::string)USERS_DB_PATH + 
"/.tmp_email.txt";
 
 3659         FILE* fp = fopen(filename.c_str(), 
"w");
 
 3662             __SS__ << 
"Could not open email file: " << filename << __E__;
 
 3672         fprintf(fp, 
"To: %s\n", toList.c_str());
 
 3673         fprintf(fp, 
"Subject: %s\n", subject.c_str());
 
 3674         fprintf(fp, 
"Content-Type: text/html\n");
 
 3675         fprintf(fp, 
"\n<html><pre>%s</pre></html>", message.c_str());
 
 3681         __COUT_WARN__ << 
"Do email was attempted, but no target users had email " 
 3682                          "addresses specified!" 
 3691 void WebUsers::addSystemMessageToMap(
const std::string& targetUser,
 
 3692                                      const std::string& fullMessage)
 
 3695     std::lock_guard<std::mutex> lock(systemMessageLock_);
 
 3697     __COUT__ << 
"Before number of users with system messages: " << systemMessages_.size()
 
 3698              << 
", first user has " 
 3699              << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
 
 3700              << 
" messages." << __E__;
 
 3702     auto it = systemMessages_.find(targetUser);
 
 3705     if(it != systemMessages_.end() && it->second.size() &&
 
 3706        it->second[it->second.size() - 1].message_ == fullMessage)
 
 3709     if(it == systemMessages_.end())  
 
 3711         systemMessages_.emplace(
 
 3712             std::pair<std::string , std::vector<SystemMessage>>(
 
 3713                 targetUser, std::vector<SystemMessage>({SystemMessage(fullMessage)})));
 
 3714         __COUTT__ << targetUser << 
" Current System Messages count = " << 1 << __E__;
 
 3719         it->second.push_back(SystemMessage(fullMessage));
 
 3720         __COUTT__ << it->first << 
" Current System Messages count = " << it->second.size()
 
 3724     __COUT__ << 
"After number of users with system messages: " << systemMessages_.size()
 
 3725              << 
", first user has " 
 3726              << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
 
 3727              << 
" messages." << __E__;
 
 3736     std::lock_guard<std::mutex> lock(systemMessageLock_);
 
 3738     __COUTT__ << 
"GetLast number of users with system messages: " 
 3739               << systemMessages_.size() << 
", first user has " 
 3740               << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
 
 3741               << 
" messages." << __E__;
 
 3743     auto it = systemMessages_.find(
"*");
 
 3744     if(it == systemMessages_.end() || it->second.size() == 0)
 
 3745         return std::make_pair(
"", 0);
 
 3747     return std::make_pair(it->second.back().message_, it->second.back().creationTime_);
 
 3756     std::string retStr = 
"";
 
 3759     std::lock_guard<std::mutex> lock(systemMessageLock_);
 
 3761     for(
auto& userSysMessages : systemMessages_)
 
 3763         for(
auto& userSysMessage : userSysMessages.second)
 
 3765             if(userSysMessage.deliveredRemote_)
 
 3770             retStr += userSysMessages.first;  
 
 3771             retStr += 
"|" + std::to_string(userSysMessage.creationTime_);
 
 3772             retStr += 
"|" + userSysMessage.message_;
 
 3773             userSysMessage.deliveredRemote_ = 
true;
 
 3788     __COUT_TYPE__(TLVL_DEBUG + 20)
 
 3789         << __COUT_HDR__ << 
"Current System Messages: " << targetUser << __E__;
 
 3790     std::string retStr = 
"";
 
 3796         std::lock_guard<std::mutex> lock(systemMessageLock_);
 
 3798         __COUT_TYPE__(TLVL_DEBUG + 20)
 
 3800             << 
"Number of users with system messages: " << systemMessages_.size()
 
 3804         auto it = systemMessages_.find(
"*");
 
 3805         for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
 
 3810             sprintf(tmp, 
"%lu", it->second[i].creationTime_);
 
 3811             retStr += std::string(tmp) + 
"|" + it->second[i].message_;
 
 3817         __COUTVS__(20, targetUser);
 
 3818         it = systemMessages_.find(targetUser);
 
 3821             for(
auto systemMessagePair : systemMessages_)
 
 3822                 __COUT_TYPE__(TLVL_DEBUG + 20)
 
 3823                     << __COUT_HDR__ << systemMessagePair.first << 
" " 
 3824                     << systemMessagePair.second.size() << 
" " 
 3825                     << (systemMessagePair.second.size()
 
 3826                             ? systemMessagePair.second[0].message_
 
 3830         if(it != systemMessages_.end())
 
 3832             __COUT_TYPE__(TLVL_DEBUG + 20)
 
 3833                 << __COUT_HDR__ << 
"Message count: " << it->second.size()
 
 3834                 << 
", Last Message: " 
 3835                 << (it->second.size() ? it->second.back().message_ : 
"") << __E__;
 
 3838         for(uint64_t i = 0; it != systemMessages_.end() && i < it->second.size(); ++i)
 
 3843             sprintf(tmp, 
"%lu", it->second[i].creationTime_);
 
 3844             retStr += std::string(tmp) + 
"|" + it->second[i].message_;
 
 3846             it->second[i].delivered_ = 
true;
 
 3851     __COUT_TYPE__(TLVL_DEBUG + 20) << __COUT_HDR__ << 
"retStr: " << retStr << __E__;
 
 3853     systemMessageCleanup();  
 
 3861 void WebUsers::systemMessageCleanup()
 
 3864     std::lock_guard<std::mutex> lock(systemMessageLock_);
 
 3866     __COUTT__ << 
"Before cleanup number of users with system messages: " 
 3867               << systemMessages_.size() << 
", first user has " 
 3868               << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
 
 3869               << 
" messages." << __E__;
 
 3870     for(
auto& userMessagesPair : systemMessages_)
 
 3872         for(uint64_t i = 0; i < userMessagesPair.second.size(); ++i)
 
 3873             if((userMessagesPair.first != 
"*" &&
 
 3874                 userMessagesPair.second[i].delivered_) ||  
 
 3878                 __COUTT__ << userMessagesPair.first
 
 3879                           << 
" at time: " << userMessagesPair.second[i].creationTime_
 
 3880                           << 
" system messages: " << userMessagesPair.second.size()
 
 3884                 userMessagesPair.second.erase(userMessagesPair.second.begin() + i);
 
 3888         __COUTT__ << 
"User '" << userMessagesPair.first
 
 3889                   << 
"' remaining system messages: " << userMessagesPair.second.size()
 
 3892     __COUTT__ << 
"After cleanup number of users with system messages: " 
 3893               << systemMessages_.size() << 
", first user has " 
 3894               << (systemMessages_.size() ? systemMessages_.begin()->second.size() : 0)
 
 3895               << 
" messages." << __E__;
 
 3903 void WebUsers::loadSecuritySelection()
 
 3905     std::string securityFileName = SECURITY_FILE_NAME;
 
 3906     FILE*       fp               = fopen(securityFileName.c_str(), 
"r");
 
 3907     char        line[100]        = 
"";
 
 3909         fgets(line, 100, fp);
 
 3913     while(i < strlen(line) && line[i] >= 
'A' && line[i] <= 
'z')
 
 3917     if(strcmp(line, SECURITY_TYPE_NONE.c_str()) == 0 ||
 
 3918        strcmp(line, SECURITY_TYPE_DIGEST_ACCESS.c_str()) == 0)
 
 3919         securityType_ = line;
 
 3921         securityType_ = SECURITY_TYPE_DEFAULT;
 
 3923     __COUT__ << 
"The current security type is " << securityType_ << __E__;
 
 3928     if(securityType_ == SECURITY_TYPE_NONE)
 
 3929         CareAboutCookieCodes_ = 
false;
 
 3931         CareAboutCookieCodes_ = 
true;
 
 3933     __COUT__ << 
"CareAboutCookieCodes_: " << CareAboutCookieCodes_ << __E__;
 
 3935     loadIPAddressSecurity();
 
 3941 void WebUsers::loadIPAddressSecurity()
 
 3943     ipAccessAccept_.clear();
 
 3944     ipAccessReject_.clear();
 
 3945     ipAccessBlacklist_.clear();
 
 3947     FILE*  fp = fopen((IP_ACCEPT_FILE).c_str(), 
"r");
 
 3953         while(fgets(line, 300, fp))
 
 3957             if(len > 2 && line[len - 1] == 
'\n')
 
 3958                 line[len - 1] = 
'\0';
 
 3959             ipAccessAccept_.emplace(line);
 
 3966     __COUTV__(ipAccessAccept_.size());
 
 3968     fp = fopen((IP_REJECT_FILE).c_str(), 
"r");
 
 3971         while(fgets(line, 300, fp))
 
 3975             if(len > 2 && line[len - 1] == 
'\n')
 
 3976                 line[len - 1] = 
'\0';
 
 3977             ipAccessReject_.emplace(line);
 
 3984     __COUTV__(ipAccessReject_.size());
 
 3986     fp = fopen((IP_BLACKLIST_FILE).c_str(), 
"r");
 
 3989         while(fgets(line, 300, fp))
 
 3993             if(len > 2 && line[len - 1] == 
'\n')
 
 3994                 line[len - 1] = 
'\0';
 
 3995             ipAccessBlacklist_.emplace(line);
 
 4002     __COUTV__(ipAccessBlacklist_.size());
 
 4006 void WebUsers::NACDisplayThread(
const std::string& nac, 
const std::string& user)
 
 4017         std::this_thread::sleep_for(std::chrono::seconds(2));
 
 4019             << 
"\n******************************************************************** " 
 4022             << 
"\n******************************************************************** " 
 4024         __COUT__ << 
"\n\nNew account code = " << nac << 
" for user: " << user << 
"\n" 
 4027             << 
"\n******************************************************************** " 
 4030             << 
"\n******************************************************************** " 
 4036 void WebUsers::deleteUserData()
 
 4038     __COUT__ << 
"$$$$$$$$$$$$$$ Deleting ALL service user data... $$$$$$$$$$$$" << __E__;
 
 4042         (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + HASHES_DB_PATH + 
"/*").c_str());
 
 4044         (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_DB_PATH + 
"/*").c_str());
 
 4046         (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_LOGIN_HISTORY_PATH + 
"/*")
 
 4049         (
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + USERS_PREFERENCES_PATH + 
"/*")
 
 4051     std::system((
"rm -rf " + (std::string)WEB_LOGIN_DB_PATH + TOOLTIP_DB_PATH).c_str());
 
 4053     std::string serviceDataPath = __ENV__(
"SERVICE_DATA_PATH");
 
 4055     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/MacroData/").c_str());
 
 4056     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/MacroHistory/").c_str());
 
 4057     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/MacroExport/").c_str());
 
 4061         (
"rm -rf " + std::string(serviceDataPath) + 
"/ConsolePreferences/").c_str());
 
 4064     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/CodeEditorData/").c_str());
 
 4067     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/OtsWizardData/").c_str());
 
 4070     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/ProgressBarData/").c_str());
 
 4073     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/RunNumber/").c_str());
 
 4074     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/RunControlData/").c_str());
 
 4077     std::system((
"rm -rf " + std::string(serviceDataPath) + 
"/VisualizerData/").c_str());
 
 4084     std::system((
"rm -rf " + std::string(__ENV__(
"LOGBOOK_DATA_PATH")) + 
"/").c_str());
 
 4086     __COUT__ << 
"$$$$$$$$$$$$$$ Successfully deleted ALL service user data $$$$$$$$$$$$" 
void copyDataChildren(HttpXmlDocument &document)
 
void removeDataElement(unsigned int dataChildIndex=0)
default to first child
 
bool loadXmlDocument(const std::string &filePath)
 
unsigned int getChildrenCount(xercesc::DOMElement *parent=0)
 
void addSystemMessage(const std::string &targetUsersCSV, const std::string &message)
 
const std::string & getSecurity(void)
WebUsers::getSecurity.
 
std::string getGenericPreference(uint64_t uid, const std::string &preferenceName, HttpXmlDocument *xmldoc=0) const
 
bool setUserWithLock(uint64_t actingUid, bool lock, const std::string &username)
 
static bool checkRequestAccess(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, WebUsers::RequestUserInfo &userInfo, bool isWizardMode=false, const std::string &wizardModeSequence="")
 
static void silenceAllUserTooltips(const std::string &username)
 
size_t getActiveUserCount(void)
 
std::map< std::string, WebUsers::permissionLevel_t > getPermissionsForUser(uint64_t uid)
from Gateway, use public version which considers remote users
 
uint64_t attemptActiveSession(const std::string &uuid, std::string &jumbledUser, const std::string &jumbledPw, std::string &newAccountCode, const std::string &ip)
 
void setGenericPreference(uint64_t uid, const std::string &preferenceName, const std::string &preferenceValue)
 
std::string getAllSystemMessages(void)
 
void cleanupExpiredEntries(std::vector< std::string > *loggedOutUsernames=0)
 
void changeSettingsForUser(uint64_t uid, const std::string &bgcolor, const std::string &dbcolor, const std::string &wincolor, const std::string &layout, const std::string &syslayout)
WebUsers::changeSettingsForUser.
 
uint64_t isCookieCodeActiveForLogin(const std::string &uuid, std::string &cookieCode, std::string &username)
 
std::string createNewLoginSession(const std::string &uuid, const std::string &ip)
 
std::string getActiveUsersString(void)
 
void createNewAccount(const std::string &username, const std::string &displayName, const std::string &email)
 
void modifyAccountSettings(uint64_t actingUid, uint8_t cmd_type, const std::string &username, const std::string &displayname, const std::string &email, const std::string &permissions)
WebUsers::modifyAccountSettings.
 
int remoteLoginVerificationPort_
Port of remote Gateway to be used for login verification.
 
bool isUsernameActive(const std::string &username) const
 
bool isUserIdActive(uint64_t uid) const
 
void saveActiveSessions(void)
 
static std::atomic< bool > remoteLoginVerificationEnabled_
true if this supervisor is under control of a remote supervisor
 
uint64_t getAdminUserID(void)
 
@ SYS_CLEANUP_WILDCARD_TIME
300 seconds
 
std::string getUsersUsername(uint64_t uid)
from Gateway, use public version which considers remote users
 
static void initializeRequestUserInfo(cgicc::Cgicc &cgi, WebUsers::RequestUserInfo &userInfo)
used by gateway and other supervisors to verify requests consistently
 
bool checkIpAccess(const std::string &ip)
 
bool xmlRequestOnGateway(cgicc::Cgicc &cgi, std::ostringstream *out, HttpXmlDocument *xmldoc, WebUsers::RequestUserInfo &userInfo)
 
uint64_t cookieCodeLogout(const std::string &cookieCode, bool logoutOtherUserSessions, uint64_t *uid=0, const std::string &ip="0")
 
std::string getSystemMessage(const std::string &targetUser)
 
uint64_t getActiveSessionCountForUser(uint64_t uid)
 
static void resetAllUserTooltips(const std::string &userNeedle="*")
WebUsers::resetAllUserTooltips.
 
static void tooltipSetNeverShowForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId, bool doNeverShow, bool temporarySilence)
 
void cleanupExpiredRemoteEntries(void)
 
std::string getUsersDisplayName(uint64_t uid)
from Gateway, use public version which considers remote users
 
void loadActiveSessions(void)
 
std::pair< std::string, time_t > getLastSystemMessage(void)
 
uint64_t attemptActiveSessionWithCert(const std::string &uuid, std::string &jumbledEmail, std::string &cookieCode, std::string &username, const std::string &ip)
 
static const std::string OTS_OWNER
defined by environment variable, e.g. experiment name
 
static void tooltipCheckForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId)
 
std::string remoteGatewaySelfName_
IP of remote Gateway to be used for login verification.
 
bool cookieCodeIsActiveForRequest(std::string &cookieCode, std::map< std::string, WebUsers::permissionLevel_t > *userPermissions=0, uint64_t *uid=0, const std::string &ip="0", bool refresh=true, bool doNotGoRemote=false, std::string *userWithLock=0, uint64_t *userSessionIndex=0)
 
void insertSettingsForUser(uint64_t uid, HttpXmlDocument *xmldoc, bool includeAccounts=false)
 
@ PERMISSION_LEVEL_ADMIN
max permission level!
 
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
 
void saveXmlDocument(const std::string &filePath)
 
void INIT_MF(const char *name)
 
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
 
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
 
static std::string exec(const char *cmd)
 
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
 
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
 
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
 
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
getMapFromString ~
 
static bool wildCardMatch(const std::string &needle, const std::string &haystack, unsigned int *priorityIndex=0)
 
static std::string decodeURIComponent(const std::string &data)
 
static std::string stackTrace(void)
 
uint64_t userSessionIndex_
can use session index to track a user's session on multiple devices/browsers
 
const WebUsers::permissionLevel_t & getGroupPermissionLevel()
 
bool setGroupPermissionLevels(const std::string &groupPermissionLevelsString)
end setGroupPermissionLevels()