Fixes #5506 (#5507)
[openemr.git] / portal / account / account.lib.php
blob790af6727536f624f7c48db47baf4df7c31bc1a6
1 <?php
3 /**
4 * Ajax Library for Register
6 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Jerry Padgett <sjpadgett@gmail.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2017-2019 Jerry Padgett <sjpadgett@gmail.com>
11 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
15 /* Library functions for register*/
17 use GuzzleHttp\Client;
18 use OpenEMR\Common\Auth\AuthHash;
19 use OpenEMR\Common\Crypto\CryptoGen;
20 use OpenEMR\Common\Logging\EventAuditLogger;
21 use OpenEMR\Common\Logging\SystemLogger;
22 use OpenEMR\Common\Twig\TwigContainer;
23 use OpenEMR\Common\Utils\RandomGenUtils;
24 use OpenEMR\FHIR\Config\ServerConfig;
26 function notifyAdmin($pid, $provider): void
29 $note = xlt("New patient registration received from patient portal. Reminder to check for possible new appointment");
30 $title = xlt("New Patient");
31 $user = sqlQueryNoLog("SELECT users.username FROM users WHERE authorized = 1 And id = ?", array($provider));
33 if (empty($user['username'])) {
34 $user['username'] = "portal-user";
37 addPnote($pid, $note, 1, 1, $title, $user['username'], '', 'New');
40 function processRecaptcha($gRecaptchaResponse): bool
42 if (empty($gRecaptchaResponse)) {
43 (new SystemLogger())->error("processRecaptcha function: gRecaptchaResponse is empty, so unable to verify recaptcha");
44 return false;
46 if (empty($GLOBALS['google_recaptcha_site_key'])) {
47 (new SystemLogger())->error("processRecaptcha function: google_recaptcha_site_key is empty, so unable to verify recaptcha");
48 return false;
50 if (empty($GLOBALS['google_recaptcha_secret_key'])) {
51 (new SystemLogger())->error("processRecaptcha function: google_recaptcha_secret_key is empty, so unable to verify recaptcha");
52 return false;
54 $googleRecaptchaSecretKey = (new CryptoGen())->decryptStandard($GLOBALS['google_recaptcha_secret_key']);
55 if (empty($googleRecaptchaSecretKey)) {
56 (new SystemLogger())->error("processRecaptcha function: decrypted google_recaptcha_secret_key global is empty, so unable to verify recaptcha");
57 return false;
60 $client = new Client([
61 'base_uri' => 'https://www.google.com/recaptcha/api/',
62 'timeout' => 2.0
63 ]);
64 $response = $client->request('POST', 'siteverify', [
65 'query' => [
66 'secret' => $googleRecaptchaSecretKey,
67 'response' => $gRecaptchaResponse
69 ]);
70 $responseArray = json_decode($response->getBody(), true);
71 (new SystemLogger())->debug("processRecaptcha function: recaptcha verification returned following", ['returnJson' => $responseArray]);
72 if (empty($responseArray)) {
73 (new SystemLogger())->debug("processRecaptcha function: recaptcha verification was unsuccessful since empty response from google");
74 return false;
76 if (empty($responseArray['success'])) {
77 (new SystemLogger())->debug("processRecaptcha function: recaptcha verification was unsuccessful since empty success status from google");
78 return false;
80 if ($responseArray['success'] === true) {
81 (new SystemLogger())->debug("processRecaptcha function: recaptcha verification was successful from host " . ($responseArray['hostname'] ?? ''));
82 return true;
83 } else {
84 (new SystemLogger())->debug("processRecaptcha function: recaptcha verification was not successful from host " . ($responseArray['hostname'] ?? ''), ['errorCodes' => ($responseArray['error-codes'] ?? '')]);
85 return false;
90 // note function only returns false when there is an error in something and does not flag if a email exists or not
91 // (this is done so a bad actor can not see if certain patients exist in the instance)
92 function verifyEmail(string $languageChoice, string $fname, string $mname, string $lname, string $dob, string $email): bool
94 if (empty($languageChoice) || empty($fname) || empty($lname) || empty($dob) || empty($email)) {
95 // only optional setting is the mname
96 (new SystemLogger())->error("a required verifyEmail function parameter is empty");
97 return false;
100 if (!validEmail($email)) {
101 (new SystemLogger())->debug("verifyEmail function is using a email that failed validEmail test, so can not use");
102 return true;
104 $twigContainer = new TwigContainer(null, $GLOBALS['kernel']);
105 $twig = $twigContainer->getTwig();
106 $templateData = [];
107 $template = 'verify-failed';
108 $emailPrepSend = false;
110 // check to ensure email not used
111 $sql = sqlQuery(
112 "SELECT `pid` FROM `patient_data` WHERE `email` = ? OR `email_direct` = ?",
114 $email,
115 $email
119 if (!empty($sql['pid'])) {
120 $templateData = ['email' => $email];
121 (new SystemLogger())->debug("verifyEmail function: the email is already in use, so can not use");
122 $emailPrepSend = true;
123 } else {
124 (new SystemLogger())->debug("verifyEmail function: the email will be used to register the patient");
126 // create token (1 hour expiry) and ensure the token is unique
127 $unique = false;
128 for ($i = 1; $i <= 10; $i++) {
129 $expiry = new DateTime('NOW');
130 $expiry->add(new DateInterval('PT01H'));
131 $token_raw = RandomGenUtils::createUniqueToken(32);
132 $token_encrypt = (new CryptoGen())->encryptStandard($token_raw);
133 if (empty($token_encrypt)) {
134 // Serious issue if this is case, so return that something bad happened.
135 (new SystemLogger())->error("OpenEMR Error : Portal email verification token encryption broken - exiting");
136 return false;
138 $token_database = $token_raw . bin2hex($expiry->format('U'));
140 $sqlVerify = sqlQueryNoLog("SELECT `id` FROM `verify_email` WHERE `token_onetime` LIKE BINARY ?", [$token_raw . '%']);
141 if (empty($sqlVerify['id'])) {
142 $unique = true;
143 break;
144 } else {
145 (new SystemLogger())->error("was unable to create a unique token in verifyEmail function, which is very odd, so will try again (will try up to 10 times)");
148 if (!$unique) {
149 (new SystemLogger())->error("was unable to create a unique token in verifyEmail function, so failed");
150 return false;
153 // place/replace database entry
154 $sql = sqlQuery("SELECT `id` FROM `verify_email` WHERE `email` = ?", [$email]);
155 if (empty($sql['id'])) {
156 sqlStatementNoLog(
157 "INSERT INTO `verify_email` (`email`, `language`, `fname`, `mname`, `lname`, `dob`, `token_onetime`, `active`, `pid_holder`) VALUES (?, ?, ?, ?, ?, ?, ?, 1, null)",
159 $email,
160 $languageChoice,
161 $fname,
162 ($mname ?? ''),
163 $lname,
164 $dob,
165 $token_database
168 } else {
169 sqlStatementNoLog(
170 "UPDATE `verify_email` SET `language` = ?, `fname` = ?, `mname` = ?, `lname` = ?, `dob` = ?, `token_onetime` = ?, `active` = 1, `pid_holder` = null WHERE `email` = ?",
172 $languageChoice,
173 $fname,
174 ($mname ?? ''),
175 $lname,
176 $dob,
177 $token_database,
178 $email
183 // create $encoded_link
184 $site_addr = $GLOBALS['portal_onsite_two_address'];
185 $site_id = $_SESSION['site_id'];
186 if (stripos($site_addr, $site_id) === false) {
187 $encoded_link = sprintf("%s?%s", attr($site_addr), http_build_query([
188 'forward_email_verify' => $token_encrypt,
189 'site' => $_SESSION['site_id']
190 ]));
191 } else {
192 $encoded_link = sprintf("%s&%s", attr($site_addr), http_build_query([
193 'forward_email_verify' => $token_encrypt
194 ]));
196 $template = 'verify-success';
197 $templateData['encoded_link'] = $encoded_link;
198 $emailPrepSend = true;
201 $htmlMessage = $twig->render('emails/patient/verify_email/message-' . $template . '.html.twig', $templateData);
202 $plainMessage = $twig->render('emails/patient/verify_email/message-' . $template . '.text.twig', $templateData);
204 if ($emailPrepSend) {
205 // send email
206 $mail = new MyMailer();
207 $email_sender = $GLOBALS['patient_reminder_sender_email'];
208 $mail->AddReplyTo($email_sender, $email_sender);
209 $mail->SetFrom($email_sender, $email_sender);
210 $mail->AddAddress($email, ($fname . ' ' . $lname));
211 $mail->Subject = xlt('Verify your email for patient portal registration');
212 $mail->MsgHTML($htmlMessage);
213 $mail->IsHTML(true);
214 $mail->AltBody = $plainMessage;
216 if ($mail->Send()) {
217 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 1, "The patient registration verification email was successfully sent to " . $email);
218 (new SystemLogger())->debug("The patient registration verification email was successfully sent to " . $email);
219 return true;
220 } else {
221 $email_status = $mail->ErrorInfo;
222 EventAuditLogger::instance()->newEvent('patient-reg-email-verify', '', '', 0, "The patient registration verification email was not successfully sent to " . $email . " because of following issue: " . $email_status);
223 (new SystemLogger())->error("The patient registration verification email was not successfully sent to " . $email . " because of following issue: " . $email_status);
224 return false;
228 // should never get to below
229 return true;
232 // note function only returns 0 when there is an error in something and does not flag if a patient exists or not
233 // (this is done so a bad actor can not see if certain patients exist in the instance)
234 function resetPassword(string $dob, string $lname, string $fname, string $email): int
236 if (empty($dob) || empty($lname) || empty($fname) || empty($email)) {
237 (new SystemLogger())->error("a resetPassword function parameter is empty");
238 return 0;
241 $sql = sqlStatement(
242 "SELECT `pid` FROM `patient_data` WHERE `dob` = ? AND `lname` = ? AND `fname` = ? AND (`email` = ? OR `email_direct` = ?)",
244 $dob,
245 $lname,
246 $fname,
247 $email,
248 $email
252 if (sqlNumRows($sql) > 1) {
253 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: Multiple patients were found in patient_data for search of: " . $fname . " " . $lname . " " . $dob . " " . $email);
254 (new SystemLogger())->error("resetPassword function selected more than 1 patient from patient_data, so was unable to reset the password");
255 return 1;
257 if (!sqlNumRows($sql)) {
258 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: No patient was found in patient_data for search of: " . $fname . " " . $lname . " " . $dob . " " . $email);
259 (new SystemLogger())->debug("resetPassword function found no patient in patient_data, so was unable to reset the password");
260 return 1;
262 $row = sqlFetchArray($sql);
263 if (empty($row['pid'])) {
264 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: No patient was found in patient_data for search of: " . $fname . " " . $lname . " " . $dob . " " . $email);
265 (new SystemLogger())->debug("resetPassword function found no patient in patient_data, so was unable to reset the password");
266 return 1;
268 $tempPid = $row['pid'];
270 $sql = sqlStatement("SELECT `pid` FROM `patient_access_onsite` WHERE `pid`=?", [$tempPid]);
271 if (sqlNumRows($sql) > 1) {
272 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: Multiple patients were found in patient_access_onsite for search of pid " . $tempPid);
273 (new SystemLogger())->error("resetPassword function selected more than 1 patient from patient_access_onsite, so was unable to reset the password");
274 return 1;
276 if (!sqlNumRows($sql)) {
277 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: No patient was found in patient_access_onsite for search of pid " . $tempPid);
278 (new SystemLogger())->debug("resetPassword function found no patient in patient_access_onsite, so was unable to reset the password");
279 return 1;
281 $row = sqlFetchArray($sql);
282 if (empty($row['pid'])) {
283 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: No patient was found in patient_access_onsite for search of pid " . $tempPid);
284 (new SystemLogger())->debug("resetPassword function found no patient in patient_access_onsite, so was unable to reset the password");
285 return 1;
288 $rtn = doCredentials($row['pid'], true, $email);
289 if ($rtn) {
290 return 1;
291 } else {
292 return 0;
296 function saveInsurance($pid)
298 newInsuranceData(
299 $pid = $pid,
300 $type = "primary",
301 $provider = "0",
302 $policy_number = $_REQUEST['policy_number'],
303 $group_number = $_REQUEST['group_number'],
304 $plan_name = $_REQUEST['provider'] . ' ' . $_REQUEST['plan_name'],
305 $subscriber_lname = "",
306 $subscriber_mname = "",
307 $subscriber_fname = "",
308 $subscriber_relationship = "",
309 $subscriber_ss = "",
310 $subscriber_DOB = "",
311 $subscriber_street = "",
312 $subscriber_postal_code = "",
313 $subscriber_city = "",
314 $subscriber_state = "",
315 $subscriber_country = "",
316 $subscriber_phone = "",
317 $subscriber_employer = "",
318 $subscriber_employer_street = "",
319 $subscriber_employer_city = "",
320 $subscriber_employer_postal_code = "",
321 $subscriber_employer_state = "",
322 $subscriber_employer_country = "",
323 $copay = $_REQUEST['copay'],
324 $subscriber_sex = "",
325 $effective_date = DateToYYYYMMDD($_REQUEST['date']),
326 $accept_assignment = "TRUE",
327 $policy_type = ""
329 newInsuranceData($pid, "secondary");
330 newInsuranceData($pid, "tertiary");
333 function validEmail($email)
335 if (preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i", $email)) {
336 return true;
339 return false;
342 // $resetPass mode return false when something breaks (although returns true if related to a patient existing or not to prevent fishing for patients)
343 // !$resetPass mode return false when something breaks (no need to protect against from fishing since can't do from registration workflow)
344 function doCredentials($pid, $resetPass = false, $resetPassEmail = ''): bool
346 $newpd = sqlQuery("SELECT id,fname,mname,lname,email,email_direct, providerID FROM `patient_data` WHERE `pid` = ?", array($pid));
347 $user = sqlQueryNoLog("SELECT users.username FROM users WHERE authorized = 1 And id = ?", array($newpd['providerID']));
349 // ensure pid exists
350 if (empty($newpd)) {
351 if ($resetPass) {
352 (new SystemLogger())->error("doCredentials function did not find a patient from patient_data for " . $pid . " (this should never happen since checked in resetPassword function), so was unable to reset the password");
353 return true;
354 } else { // !$resetPass
355 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "Patient credential creation failure: Following pid did not exist: " . $pid);
356 (new SystemLogger())->error("doCredentials function did not find a patient from patient_data for " . $pid . " , so was unable to create credentials");
357 return false;
361 // ensure email is valid
362 if ($resetPass) {
363 if ((empty($resetPassEmail)) || ((($newpd['email'] ?? '') != $resetPassEmail) && (($newpd['email_direct'] ?? '') != $resetPassEmail))) {
364 (new SystemLogger())->error("doCredentials function with empty email or unable to find correct email " . $resetPassEmail . " in patient from patient_data for pid " . $pid . " (this should never happen since checked in resetPassword function), so was unable to reset the password");
365 return true;
367 if (!validEmail($resetPassEmail)) {
368 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: Email " . $resetPassEmail . " was not considered valid for pid: " . $pid);
369 (new SystemLogger())->error("doCredentials function with email " . $resetPassEmail . " for pid " . $pid . " that was not valid per validEmail function, so was unable to reset the password");
370 return false;
372 $newpd['email'] = $resetPassEmail;
373 } else { // !$resetPass
374 if (!validEmail($newpd['email'])) {
375 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "Patient password reset failure: Email " . $newpd['email'] . " was not considered valid for pid: " . $pid);
376 (new SystemLogger())->error("doCredentials function with email " . $newpd['email'] . " for pid " . $pid . " was not valid per validEmail function, so was unable to complete the registration");
377 return false;
381 // Token expiry 1 hour
382 $expiry = new DateTime('NOW');
383 $expiry->add(new DateInterval('PT01H'));
385 $token_new = RandomGenUtils::createUniqueToken(32);
386 $pin = RandomGenUtils::createUniqueToken(6);
387 if (!$resetPass) {
388 $clear_pass = RandomGenUtils::generatePortalPassword();
389 $uname = $newpd['fname'] . $newpd['id'];
392 // Will send a link to user with encrypted token
393 $token = (new CryptoGen())->encryptStandard($token_new);
394 if (empty($token)) {
395 // Serious issue if this is case, so exit.
396 if ($resetPass) {
397 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure secondary critical encryption error for email " . $newpd['email'] . " and pid: " . $pid);
398 (new SystemLogger())->error("Error : Token encryption failed during patient password reset - exiting");
399 return false;
400 } else { // !$resetPass
401 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "Patient credential creation registration failure secondary critical encryption error for email " . $newpd['email'] . " and pid: " . $pid);
402 (new SystemLogger())->error("Error : Token encryption failed during patient registration - exiting");
403 return false;
406 $site_addr = $GLOBALS['portal_onsite_two_address'];
407 $site_id = $_SESSION['site_id'];
408 if (stripos($site_addr, $site_id) === false) {
409 $encoded_link = sprintf("%s?%s", attr($site_addr), http_build_query([
410 'forward' => $token,
411 'site' => $_SESSION['site_id']
412 ]));
413 } else {
414 $encoded_link = sprintf("%s&%s", attr($site_addr), http_build_query([
415 'forward' => $token
416 ]));
419 if (!$resetPass) {
420 $newHash = (new AuthHash('auth'))->passwordHash($clear_pass);
421 if (empty($newHash)) {
422 // Serious issue if this is case, so exit.
423 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "Patient credential creation registration failure secondary critical hashing error for email " . $newpd['email'] . " and pid: " . $pid);
424 (new SystemLogger())->error("Error : Hashing failed during patient registration - exiting");
425 return false;
429 // Will store unencrypted token in database with the pin and expiration date
430 $one_time = $token_new . $pin . bin2hex($expiry->format('U'));
431 if ($resetPass) {
432 // already confirmed there is an entry in patient_access_onsite in previously called resetPassword function
433 // (note that portal_username, portal_pwd_status and portal_pwd are not touched here since password needs to remain valid until patient
434 // actually changes the password)
435 $query_parameters = [$one_time, $pid];
436 sqlStatementNoLog("UPDATE `patient_access_onsite` SET `portal_onetime` = ? WHERE `pid` = ?", $query_parameters);
437 } else { // !$resetPass
438 $query_parameters = [$uname, $one_time, $newHash, $pid];
439 $res = sqlStatement("SELECT `id` FROM `patient_access_onsite` WHERE `pid` = ?", [$pid]);
440 if (sqlNumRows($res)) {
441 // this should never happen in current use case where these credentials are created after a new patient registers, so will return error
442 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "Patient credential creation registration failure secondary to credentials already existing for email " . $newpd['email'] . " and pid: " . $pid);
443 (new SystemLogger())->error("OpenEMR Error : doCredentials for registration - already credentials exists, so unable to create new credentials.");
444 return false;
445 } else {
446 sqlStatementNoLog("INSERT INTO patient_access_onsite SET portal_username=?,portal_onetime=?,portal_pwd=?,portal_pwd_status=0,pid=?", $query_parameters);
450 $twigContainer = new TwigContainer(null, $GLOBALS['kernel']);
451 $twig = $twigContainer->getTwig();
452 $fhirServerConfig = new ServerConfig();
454 $data = [
455 'portal_onsite_two_address' => $GLOBALS['portal_onsite_two_address']
456 ,'pin' => $pin
457 ,'encoded_link' => $encoded_link
458 ,'fhir_address' => $fhirServerConfig->getFhirUrl()
459 ,'fhir_requirements_address' => $fhirServerConfig->getFhir3rdPartyAppRequirementsDocument()
461 $htmlMessage = $twig->render('emails/patient/reset_credentials/message.html.twig', $data);
462 $plainMessage = $twig->render('emails/patient/reset_credentials/message.text.twig', $data);
464 $mail = new MyMailer();
465 $pt_name = text($newpd['fname'] . ' ' . $newpd['lname']);
466 $pt_email = text($newpd['email']);
467 $email_subject = xlt('Access Your Patient Portal') . ' / ' . xlt('3rd Party API Access');
468 $email_sender = $GLOBALS['patient_reminder_sender_email'];
469 $mail->AddReplyTo($email_sender, $email_sender);
470 $mail->SetFrom($email_sender, $email_sender);
471 $mail->AddAddress($pt_email, $pt_name);
472 $mail->Subject = $email_subject;
473 $mail->MsgHTML($htmlMessage);
474 $mail->IsHTML(true);
475 $mail->AltBody = $plainMessage;
477 if ($mail->Send()) {
478 if ($resetPass) {
479 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 1, "The patient reset email was successfully sent to " . $newpd['email'] . " for pid " . $pid . ".");
480 (new SystemLogger())->debug("The patient reset email was successfully sent to " . $newpd['email'] . " for pid " . $pid . ".");
481 } else { // !$resetPass
482 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 1, "The patient registration credentials email was successfully sent to " . $newpd['email'] . " for pid " . $pid . ".");
483 (new SystemLogger())->debug("The patient registration credentials email was successfully sent to " . $newpd['email'] . " for pid " . $pid . ".");
485 return true;
486 } else {
487 $email_status = $mail->ErrorInfo;
488 if ($resetPass) {
489 EventAuditLogger::instance()->newEvent('patient-password-reset', '', '', 0, "Patient password reset failure: The reset email to " . $newpd['email'] . " for pid " . $pid . " was not successful because of following issue: " . $email_status);
490 (new SystemLogger())->error("Patient password reset failure: The reset email to " . $newpd['email'] . " for pid " . $pid . " was not successful because of following issue: " . $email_status);
491 } else { // !$resetPass
492 EventAuditLogger::instance()->newEvent('patient-registration', '', '', 0, "The patient registration credentials email was not successfully sent to " . $newpd['email'] . " for pid " . $pid . " because of following issue: " . $email_status);
493 (new SystemLogger())->error("The patient registration credentials email was not successfully sent to " . $newpd['email'] . " for pid " . $pid . " because of following issue: " . $email_status);
494 // notify admin of failure.
495 $title = xlt("Failed Registration");
496 $admin_msg = "\n" . xlt("A new patients credentials could not be sent after portal registration.");
497 $admin_msg .= "\n" . "EMAIL ERROR: " . $email_status;
498 $admin_msg .= "\n" . xlt("Please follow up.");
499 // send note
500 addPnote($pid, $admin_msg, 1, 1, $title, $user['username'], '', 'New');
502 return false;
506 // the race condition can happen in registration since basically submitting patient info and insurance info at same time
507 // where the pid is created and stored by the patient info. so, will sleep 1 seconds prior first attempt and then 5 seconds
508 // prior second attempt to allow things to work out. Can make this mechanism more sophisticated in future if needed. In the
509 // case of insurance, if it does fail getting the pid for some reason then the registration will still happen (and will
510 // just not store the insurance info in worst case scenario).
511 function getPidHolder($preventRaceCondition = false): int
513 if (empty($_SESSION['token_id_holder'])) {
514 (new SystemLogger())->debug("getPidHolder function failed because token_id_holder session variable was not set");
515 return 0;
517 if ($preventRaceCondition) {
518 sleep(1);
520 $sql = sqlQueryNoLog("SELECT `pid_holder` FROM `verify_email` WHERE `id` = ?", [$_SESSION['token_id_holder']]);
521 if (!empty($sql['pid_holder'])) {
522 return $sql['pid_holder'];
523 } else {
524 if (!$preventRaceCondition) {
525 return 0;
526 } else { // $preventRaceCondition
527 (new SystemLogger())->debug("getPidHolder function sleeping fo 5 seconds to deal with race condition");
528 sleep(5);
529 return getPidHolder();
534 function cleanupRegistrationSession()
536 unset($_SESSION['patient_portal_onsite_two']);
537 unset($_SESSION['authUser']);
538 unset($_SESSION['pid']);
539 unset($_SESSION['site_id']);
540 unset($_SESSION['register']);
541 unset($_SESSION['register_silo_ajax']);
542 OpenEMR\Common\Session\SessionUtil::portalSessionCookieDestroy();