Odyssey
cu_notify.data
1 <?php
2 /**
3  * FILE: cu_notify.data
4  * PURPOSE: pull the data functions out of cu_notify.prg in order to use data test cases against them.
5  */
6 
7 /**
8  * function GetTemporaryEmail()
9  * Get the email to use when a CU email has not been set up yet.
10  *
11  * @return string
12  */
13 function GetTemporaryEmail() {
14  return "unattended@homecu.com";
15 }
16 
17 /**
18  * function UpdateVerifiedEmailList($dbh, $Cu, $emailList)
19  * This is called with the "update" button on the popup for "Manage Email List." It is going to:
20  * 1) Remove emails from AWS SES that were removed from the kendoGrid.
21  * 2) Send verification emails to new emails added to the grid.
22  * 3) Save the entire list into the database under the "SES" role.
23  * If there are any errors, the previous emailList will show up with the error message.
24  *
25  * @param $dbh -- the database connection.
26  * @param $Cu -- the credit union.
27  * @param $emailList -- the email list from the kendoGrid. (dataSource.data()) "Deleted" emails are in the kendoGrid's data but are filtered out in the display.
28  *
29  * @return "status" -- "000" if successful, nonzero otherwise.
30  * @return "error" -- The error message returned with the status code.
31  * @return "verifiedEmailList" -- If the function is successful, this will be the email list constructed from the changes (adds and removals.)
32  * Otherwise, this will be the starting $emailList.
33  */
34 function UpdateVerifiedEmailList($dbh, $Cu, $emailList) {
35 
36  try {
37 
38  $results = _TransformKendoEmailList($emailList);
39  if ($results ["status"] !== "000") {
40  throw new exception($results ["error"], 12);
41  }
42  extract($results["data"]);
43 
44  // 2) Check to see if any of the emails are being used by the manage script. Set the "used" flag to true.
45  $results = CheckIfEmailIsUsed($dbh, $Cu, $allEmails);
46  if ($results["status"] !== "000") {
47  throw new exception ($results["error"], 9);
48  }
49 
50  $results = _ModifyVerifiedEmailListWithUsedEmails ($results["usedEmailList"], $verifiedEmailList, $deletedEmails);
51  if ($results ["status"] !== "000") {
52  throw new exception ($results["error"], 14);
53  }
54  extract($results["data"]);
55 
56  if (count($unverifiedEmails) > 0) {
57  // 3) Send emails with status of empty to Amazon. Set them to "Pending" for the grid and verified = false in the database.
58  $pyFile = dirname(__FILE__) . "/../../shared/scripts/verifySESEmail.py";
59  $pyAction = "verifyEmails";
60  $cmd = "python3 $pyFile -a $pyAction -e " . '"' . implode(" ", $unverifiedEmails) . '"';
61  $response = HCU_JsonDecode(exec($cmd));
62  if ($response["status"] !== "000") {
63  throw new exception ($response["error"], 8);
64  }
65  }
66 
67  if (count($deletedEmails) > 0) {
68  // 4) Send deleted emails to Amazon so that Amazon can remove the identities.
69  $pyFile = dirname(__FILE__) . "/../../shared/scripts/verifySESEmail.py";
70  $pyAction = "removeEmails";
71  $cmd = "python3 $pyFile -a $pyAction -e " . '"' . implode(" ", $deletedEmails) . '"';
72  $response = HCU_JsonDecode(exec($cmd));
73  if ($response["status"] !== "000") {
74  throw new exception ($response["error"], 10);
75  }
76  }
77 
78  // 5) Check if SES role exists for CU.
79  $sql = "select email from cuadmnotify where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
80  $sth = db_query($sql, $dbh);
81  if (!$sth) {
82  throw new exception("Email query failed.", 5);
83  }
84 
85  // 6) If role exists
86  if (db_num_rows($sth) > 0) {
87 
88  // 6a) Update role with new values from the grid.
89  $sql = "update cuadmnotify set email = '" . prep_save(HCU_JsonEncode(array_values($databaseEmailList))) . "'
90  where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
91  $sth = db_query($sql, $dbh);
92  if (!$sth) {
93  throw new exception ("Update query failed.", 6);
94  }
95 
96  // 7) If role doesn't exist
97  } else {
98 
99  // 7a) Create role with new values from the grid.
100  $sql = "insert into cuadmnotify (cu, role, email) values ('" . prep_save($Cu, 10) . "', 'SES', '" .
101  prep_save(HCU_JsonEncode(array_values($databaseEmailList))) . "')";
102  $sth = db_query($sql, $dbh);
103  if (!$sth) {
104  throw new exception ("Insert query failed.", 7);
105  }
106  }
107 
108  // 8) Sort
109  ksort($verifiedEmailList);
110 
111  $results = _GetInfoForEmailList($deletedEmails, $unverifiedEmails);
112  if ($results ["status"] !== "000") {
113  throw new exception ($results ["error"], 13);
114  }
115  extract($results ["data"]);
116 
117  $returnArray = array("status" => "000", "error" => "", "verifiedEmailList" => array_values($verifiedEmailList), "info" => $info, "operation" => "update");
118  } catch (exception $e) {
119 
120  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "operation" => "update");
121  $results = ReadVerifiedEmailList($dbh, $Cu);
122  $returnArray["verifiedEmailList"] = $results["verifiedEmailList"];
123  }
124 
125  return $returnArray;
126 }
127 
128 /**
129  * function _ModifyVerifiedEmailListWithUsedEmails ($usedEmailList, $verifiedEmailList, $deletedEmails)
130  * Add "used" flags to the verifiedEmailList from the usedEmailList.
131  *
132  * @param $usedEmailList -- a map of emails that are used in the CU email list main grid or in the email template.
133  * @param $verifiedEmailList -- a list of emails for the kendoGrid.
134  * @param $deletedEmails -- a list of emails to be deleted from the database and from AWS.
135  *
136  * @return "status" -- "000" if successful, nonzero if there is an error.
137  * @return "error" -- "" if successful, nonempty if there is an error.
138  * @return "data"."verifiedEmailList" -- the verified email list with the "used" column updated.
139  */
140 function _ModifyVerifiedEmailListWithUsedEmails ($usedEmailList, $verifiedEmailList, $deletedEmails) {
141  try {
142  if (!isset($usedEmailList) || !is_array($usedEmailList) || !isset($verifiedEmailList) || !is_array($verifiedEmailList)
143  || !isset($deletedEmails) || !is_array($deletedEmails)) {
144  throw new exception ("Inputs are not valid.", 12);
145  }
146 
147  foreach($usedEmailList as $emailKey => $emailRow) {
148  if (!HCU_array_key_exists("email", $emailRow) || !HCU_array_key_exists("used", $emailRow)) {
149  throw new exception ("UsedEmailList is not valid.", 13);
150  }
151  $email = $emailRow["email"];
152  $used = $emailRow["used"];
153  if (!validateEmail($email)) {
154  throw new exception ("One or more emails are not valid.", 14);
155  }
156  if (HCU_array_key_exists($emailKey, $verifiedEmailList)) {
157  $verifiedEmailList[$emailKey]["used"] = $used;
158  } else if ($used && HCU_array_key_exists($emailKey, $deletedEmails)) {
159  throw new exception ("Cannot delete an email in use.", 11);
160  }
161  }
162 
163  $returnArray = array("status" => "000", "error" => "", "data" => array("verifiedEmailList" => $verifiedEmailList));
164  } catch (exception $e) {
165  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
166  }
167 
168  return $returnArray;
169 }
170 
171 /**
172  * function _GetInfoForEmailList($deletedEmails, $unverifiedEmails)
173  * Constructs the information to return to the user. This will tell which emails where added or deleted (if any.)
174  *
175  * @param $deletedEmails -- a list of emails to be deleted from the database and from AWS.
176  * @param $unverifiedEmails -- a list of emails to send verification emails.
177  *
178  * @return "status" -- "000" if successful, nonzero if there is an error.
179  * @return "error" -- "" if successful, nonempty if there is an error.
180  * @return "data"."info" -- the full success message.
181  */
182 function _GetInfoForEmailList($deletedEmails, $unverifiedEmails) {
183  try {
184  // 9) Info
185  $info = array();
186  $info[] = "Email list saved successfully.";
187  if (count ($unverifiedEmails) > 0) {
188  $info[] = "&nbsp;";
189  $info[] = "Verification emails were sent to:";
190  $info = array_merge($info, array_keys($unverifiedEmails));
191  }
192 
193  if (count($deletedEmails) > 0) {
194  $info[] = "&nbsp;";
195  $info[] = "The following emails were removed:";
196  $info = array_merge($info, array_keys($deletedEmails));
197  }
198 
199  $returnArray = array("status" => "000", "error" => "", "data" => array("info" => $info));
200  } catch (exception $e) {
201  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
202  }
203  return $returnArray;
204 }
205 
206 /**
207  * function _TransformKendoEmailList($emailList)
208  * This transforms the input of a kendoGrid into what it looks like after the update.
209  * It also creates multiple variables to send for verification, deletion, and what to save in the database.
210  *
211  * @param $emailList -- the input from the kendoGrid. This is a JSON-encoded string.
212  *
213  * @return "status" -- "000" if successful, nonzero if there is an error.
214  * @return "error" -- "" if successful, nonempty if there is an error.
215  * @return "data"."allEmails" -- a list of all emails. This will be sent to the CheckIfEmailIsUsed function to see if it can be deleted.
216  * @return "data"."unverifiedEmails" -- a list of new emails. This will be sent to the python script to send out verification emails.
217  * @return "data"."verifiedEmailList" -- a list of emails for the kendoGrid. This will eventually have the "used" attribute set.
218  * Any "unverifiedEmails" are set to a status of "Pending."
219  * @return "data"."databaseEmailList" -- a list of emails for the database.
220  * The only other attribute is "verified" which is true if the email is verified and false otherwise.
221  * @return "data"."deletedEmails" -- a list of emails to delete. These don't show up in the databaseEmailList or the verifiedEmailList.
222  * They will be sent to the python script to delete.
223  */
224 function _TransformKendoEmailList($emailList) {
225  $verifiedEmailList = array();
226  $databaseEmailList = array();
227  $unverifiedEmails = array();
228  $deletedEmails = array();
229  $allEmails = array();
230  $validStatuses = array("Success", "Pending", "Failed", "TemporaryFailure", "NotStarted", "");
231  try {
232  // 1) Extract and verify emailList. Build an array for the database and for the grid.
233  $emailList = HCU_JsonDecode($emailList);
234  if (!is_array($emailList) || count($emailList) == 0) {
235  throw new exception ("Email list is not valid.", 1);
236  }
237 
238  foreach($emailList as $emailRow) {
239  if (!HCU_array_key_exists("email", $emailRow) || !HCU_array_key_exists("status", $emailRow)) {
240  throw new exception ("Email list is not valid.", 2);
241  }
242  $email = $emailRow["email"];
243  $status = $emailRow["status"];
244  $emailKey = strtolower($email);
245 
246  if (!in_array($status, $validStatuses)) {
247  throw new exception ("Email list is not valid.", 3);
248  }
249 
250  if (!validateEmail($email)) {
251  throw new exception ("Email list is not valid.", 4);
252  }
253 
254  // At this point, the temporary email should never gotten this far but if it is, filter it out so it doesn't save it.
255  if (trim(strtolower($email)) == GetTemporaryEmail()) {
256  continue;
257  }
258 
259  $allEmails[] = prep_save($email);
260  if (!HCU_array_key_value("deleted", $emailRow)) {
261  if ($status == "") {
262  $unverifiedEmails [$emailKey] = str_replace('"', '\x22', $emailKey);
263  // escapeshellarg() does too much. It will escape all the quotes in the command.
264  $verifiedEmailList [$emailKey] = array("email" => $emailKey, "status" => "Pending");
265 
266  $date = new DateTime("now", new DateTimeZone("UTC"));
267  $date = $date->format("U");
268  $databaseEmailList [$emailKey] = array("email" => $emailKey, "verified" => false, "date" => intval($date));
269  } else if ($status == "Success") {
270  $verifiedEmailList [$emailKey] = array("email" => $emailKey, "status" => $status);
271  $databaseEmailList [$emailKey] = array("email" => $emailKey, "verified" => true);
272  } else {
273  $verifiedEmailList [$emailKey] = array("email" => $emailKey, "status" => $status);
274  $databaseEmailList [$emailKey] = array("email" => $emailKey, "verified" => false);
275  }
276  } else if (!HCU_array_key_value("itsNew", $emailRow)) { // It should be already filtered out on the frontend side.
277  $deletedEmails [$emailKey] = str_replace('"', '\x22', $emailKey); // escapeshellarg() does too much. It will escape all the quotes in the command.
278  }
279  }
280 
281  // 1.5) Now remove any deleted emails that are in the verified ones. (User deleted and readded email.)
282  foreach ($deletedEmails as $email => $value) {
283  if (HCU_array_key_exists($email, $verifiedEmailList)) {
284  unset($deletedEmails[$email]);
285  }
286  }
287 
288  $returnArray = array("status" => "000", "error" => "",
289  "data" => array("allEmails" => $allEmails, "unverifiedEmails" => $unverifiedEmails, "verifiedEmailList" => $verifiedEmailList,
290  "databaseEmailList" => $databaseEmailList, "deletedEmails" => $deletedEmails));
291  } catch (exception $e) {
292  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
293  }
294  return $returnArray;
295 }
296 
297 /**
298  * function ReadVerifiedEmailList($dbh, $Cu)
299  * This function will check the SES role for the credit union.
300  * If it exists, then it will check the statuses from Amazon for any unverified emails and return a list of emails and statuses.
301  * If it doesn't exist, then it will get a list of emails used for CU emails and return those.
302  * (Statuses will be empty because they normally won't be sent to AWS SES at this point.)
303  *
304  * @param $dbh -- the database connection
305  * @param $Cu -- the credit union
306  *
307  * @return "status" -- "000" if successful, nonzero otherwise.
308  * @return "error" -- The error message returned with the status code.
309  * @return "verifiedEmailList" -- If the function is successful, this will the emails from the SES role or the emails in usage in CU emails.
310  */
311 function ReadVerifiedEmailList($dbh, $Cu) {
312  $verifiedEmailList = array();
313  $databaseEmailList = array();
314  $updateDatabase = false; // This is needed when an email becomes verified.
315  $isNew = false;
316  $approvedEmails = array();
317  try {
318 
319  // 1) Check if SES role exists for CU.
320  $sql = "select email from cuadmnotify where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
321  $sth = db_query($sql, $dbh);
322  if (!$sth) {
323  throw new exception("Email query failed.", 1);
324  }
325 
326  // 2) If role exists
327  if (db_num_rows($sth) > 0) {
328 
329  $results = _TransformDatabaseEmailList(db_fetch_assoc($sth, 0)["email"]);
330  if ($results ["status"] !== "000") {
331  throw new exception ($results ["error"], 13);
332  }
333  extract($results ["data"]);
334 
335  // 2c) Check to see if any of the emails are being used by the manage script. Set the "used" flag to true.
336  $results = CheckIfEmailIsUsed($dbh, $Cu, $allEmails);
337  if ($results["status"] !== "000") {
338  throw new exception ($results["error"], 9);
339  }
340  foreach($results["usedEmailList"] as $emailRow) {
341  $key = strtolower($emailRow["email"]);
342  $verified = $databaseEmailList[$key]["verified"];
343  $verifiedEmailList[$key]["used"] = $emailRow["used"] && $verified;
344  }
345 
346  // 2d) Call python script to get statuses of non-verified emails.
347  if (count($unverifiedEmails) > 0) {
348  $pyFile = dirname(__FILE__) . "/../../shared/scripts/verifySESEmail.py";
349  $pyAction = "getVerification";
350 
351  $cmd = "python3 $pyFile -a $pyAction -e " . '"' . implode(" ", $unverifiedEmails) . '"';
352  $response = exec($cmd);
353 
354  $results = _ValidateAWSStatusReturn($response);
355  if ($results ["status"] !== "000") {
356  throw new exception ($results ["error"], 14);
357  }
358  extract($results ["data"]);
359 
360  $results = _TransformEmailStatuses($emailStatuses, $verifiedEmailList, $databaseEmailList);
361  if ($results ["status"] !== "000") {
362  throw new exception ($results ["error"], 15);
363  }
364  extract($results ["data"]);
365  }
366 
367  // 2f) Update the "SES" role if one or more emails became verified.
368  if ($updateDatabase) {
369 
370  foreach($databaseEmailList as $datum) {
371  if ($datum["verified"]) {
372  $approvedEmails[] = array("email" => $datum["email"]);
373  }
374  }
375  $sql = "update cuadmnotify set email = '" . prep_save(HCU_JsonEncode(array_values($databaseEmailList))) .
376  "' where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
377  $sth = db_query($sql, $dbh);
378  if (!$sth) {
379  throw new exception ("Update query failed.", 10);
380  }
381  }
382 
383  // 3) If role doesn't exist
384  } else {
385  $isNew = true;
386  // 3a) Retrieve distinct emails from the other roles in the table.
387  $results = ReadEmailNotify($dbh, $Cu);
388  if ($results["status"] !== "000") {
389  throw new exception($results["error"], 7);
390  }
391  $dataFromNotifies = $results ["data"]["gridData"];
392 
393  // 3b) Get emails out of email template for broadcast emails.
394  $sql = "select distinct trim(tmpl_email) as email from cuadmemailtmpl where cu = '" . prep_save($Cu, 10) . "'";
395  $sth = db_query($sql, $dbh);
396  if (!$sth) {
397  throw new exception ("Email template failed.", 12);
398  }
399  $dataFromTemplate = db_fetch_all($sth);
400 
401  $results = _TransformEmailsNoSES($dataFromNotifies, $dataFromTemplate);
402  if ($results ["status"] !== "000") {
403  throw new exception ($results ["error"], 16);
404  }
405 
406  extract($results["data"]);
407  }
408 
409  // Add temporary email to end of list.
410  $approvedEmails[] = array("email" => GetTemporaryEmail());
411 
412  $returnArray = array("status" => "000", "error" => "", "approvedEmails" => $approvedEmails);
413  } catch (exception $e) {
414  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "approvedEmails" => $approvedEmails);
415  }
416 
417  // 4) Sort
418  ksort($verifiedEmailList);
419  $returnArray ["verifiedEmailList"] = array_values($verifiedEmailList);
420  $returnArray ["isNew"] = $isNew;
421  $returnArray ["operation"] = "read";
422 
423  return $returnArray;
424 }
425 
426 /**
427  * function _TransformEmailsNoSES($dataFromNotifies, $dataFromTemplate)
428  * This function creates a list to send to the kendoGrid when there is no SES role in the database.
429  * Data comes from two sources: emails of other roles that are not hidden and email templates.
430  *
431  * @param $dataFromNotifies -- Emails from the cuadmnotify table that are not hidden.
432  * @param $dataFromTemplate -- Emails from the cuadmemailtmpl table.
433  *
434  * @return "status" -- "000" if successful, nonzero if there is an error.
435  * @return "error" -- "" if successful, nonempty if there is an error.
436  * @return "data"."verifiedEmailList" -- the data for the kendoGrid.
437  */
438 function _TransformEmailsNoSES($dataFromNotifies, $dataFromTemplate) {
439  $verifiedEmailList = array();
440  try {
441  foreach($dataFromNotifies as $emailRow) {
442  // Only the from emails need to be validated.
443  if (!$emailRow["single"]) {
444  continue;
445  }
446 
447  $email = trim($emailRow["email"]);
448  $emailKey = strtolower($email); // Emails are case insensitive.
449 
450  // Do not include any temporary emails.
451  if ($emailKey == GetTemporaryEmail()) {
452  continue;
453  }
454 
455  if ($email != "" && !HCU_array_key_exists($emailKey, $verifiedEmailList)) {
456  $verifiedEmailList[$emailKey] = array("email" => $emailKey, "status" => "", "used" => true, "isDirty" => true);
457  }
458  }
459 
460  if ($dataFromTemplate) {
461  foreach($dataFromTemplate as $row) {
462  $email = $row["email"];
463  $emailKey = strtolower($email);
464 
465  if (!HCU_array_key_exists($emailKey, $verifiedEmailList)) {
466  $verifiedEmailList[$emailKey] = array("email" => $emailKey, "status" => "", "used" => true, "isDirty" => true);
467  }
468  }
469  }
470 
471  $returnArray = array("status" => "000", "error" => "", "data" => array("verifiedEmailList" => $verifiedEmailList));
472 
473  } catch (exception $e) {
474  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
475  }
476  return $returnArray;
477 }
478 
479 /**
480  * function _TransformEmailStatuses($emailStatuses, $verifiedEmailList, $databaseEmailList)
481  * This modifies the kendoGrid data to include the status data and the database data to be "verified": true when the status becomes "Success."
482  *
483  * @param $emailStatuses -- the email statuses section of AWS call.
484  * @param $verifiedEmailList -- the kendoGrid data to modify.
485  * @param $databaseEmailList -- the database data to modify.
486  *
487  * @return "status" -- "000" if successful, nonzero if there is an error.
488  * @return "error" -- "" if successful, nonempty if there is an error.
489  * @return "data"."verifiedEmailList" -- the data for the kendoGrid.
490  * @return "data"."databaseEmailList" -- the data for the database.
491  * @return "data"."updateDatabase" -- this is a boolean. When true, update the database. The only case is when one of the verified attributes change.
492  */
493 function _TransformEmailStatuses($emailStatuses, $verifiedEmailList, $databaseEmailList) {
494  $updateDatabase = false;
495  try {
496  foreach($emailStatuses as $email => $emailRow) {
497  $emailKey = strtolower($email); // Emails are case insensitive.
498  $status = HCU_array_key_value("VerificationStatus", $emailRow);
499 
500  if ($status !== false) {
501  $verifiedEmailList[$emailKey]["status"] = $status;
502  $databaseEmailList[$emailKey]["email"] = $emailKey;
503 
504  if ($status == "Success") { // If the status is "Success", then update the verified option. Otherwise, it wouldn't show up as an option later.
505  $databaseEmailList[$emailKey]["verified"] = true;
506  $verifiedEmailList[$emailKey]["resend"] = false; // Do not need to resend if verified.
507  unset($databaseEmailList[$emailKey]["date"]); // Date is no longer important.
508  }
509  $updateDatabase = true;
510  }
511  }
512 
513  $returnArray = array("status" => "000", "error" => "", "data" => array("verifiedEmailList" => $verifiedEmailList, "databaseEmailList" => $databaseEmailList,
514  "updateDatabase" => $updateDatabase));
515  } catch (exception $e) {
516  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
517  }
518  return $returnArray;
519 }
520 
521 /**
522  * function _TransformDatabaseEmailList($emailList)
523  * This gets the kendoGrid data when coming from the database. (Read.)
524  *
525  * @param $emailList -- the email list from the SES role in the database.
526  *
527  * @return "status" -- "000" if successful, nonzero if there is an error.
528  * @return "error" -- "" if successful, nonempty if there is an error.
529  * @return "data"."allEmails" -- a list of all emails. This will be sent to the CheckIfEmailIsUsed function to see if it can be deleted.
530  * @return "data"."unverifiedEmails" -- a list of new emails. This will be sent to the python script to send out verification emails.
531  * @return "data"."verifiedEmailList" -- a list of emails for the kendoGrid. This will eventually have the "used" attribute set.
532  * Any "unverifiedEmails" are set to a status of "Pending."
533  * @return "data"."databaseEmailList" -- a list of emails for the database.
534  * The only other attribute is "verified" which is true if the email is verified and false otherwise.
535  */
536 function _TransformDatabaseEmailList($emailList) {
537  $unverifiedEmails = array();
538  $allEmails = array();
539  $verifiedEmailList = array();
540  $databaseEmailList = array();
541  try {
542  // 2a) Retrieve emails from role with verified boolean.
543  $emailList = HCU_JsonDecode($emailList);
544 
545  $utc = new DateTimeZone("UTC");
546  $hourAgo = new DateTime("now", $utc);
547  $hourAgo->modify('-1 hour');
548 
549  // 2b) Create array for grid with emails and statuses. Set status to "Success" for verified emails.
550  foreach($emailList as $emailRow) {
551  $email = trim($emailRow["email"]);
552  $emailKey = strtolower($email); // Emails are case insensitive.
553 
554  if (!HCU_array_key_exists($emailKey, $verifiedEmailList)) {
555  if (!validateEmail($emailKey)) {
556  throw new exception ("Email list is not valid.", 8);
557  }
558 
559  // Should not have gotten the temporary email in the database for the SES role but if that is the case, filter it out.
560  if ($emailKey == GetTemporaryEmail()) {
561  continue;
562  }
563 
564  // Force emails to be lowercase regardless
565  $emailRow["email"] = $emailKey;
566 
567  $date = HCU_array_key_value("date", $emailRow);
568  if ($date === false) {
569  $resend = true;
570  } else {
571  $date = DateTime::createFromFormat("U", $date, $utc);
572  $resend = $date < $hourAgo;
573  }
574 
575  $verifiedEmailList[$emailKey] = array("email" => $emailKey, "status" => "", "used" => false, "resend" => $resend);
576  $allEmails[$emailKey] = prep_save($emailKey);
577  if ($emailRow["verified"] === true) {
578  $verifiedEmailList[$emailKey]["status"] = "Success";
579  $verifiedEmailList[$emailKey]["resend"] = false; // Do not need to resend if verified.
580  } else {
581  $unverifiedEmails[$emailKey] = str_replace('"', '\x22', $emailKey);
582  // escapeshellarg() does too much. It will escape all the quotes in the command.
583  }
584  $databaseEmailList[$emailKey] = $emailRow;
585  }
586  }
587 
588  $returnArray = array("status" => "000", "error" => "",
589  "data" => array("unverifiedEmails" => $unverifiedEmails, "allEmails" => $allEmails, "verifiedEmailList" => $verifiedEmailList,
590  "databaseEmailList" => $databaseEmailList));
591  } catch (exception $e) {
592  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
593  }
594 
595  return $returnArray;
596 }
597 
598 /**
599  * function _ValidateAWSStatusReturn($response)
600  * This ensures that the AWS call to get statuses comes back correctly and retrieves the email statuses from the response.
601  *
602  * @param $response -- the response from the python script.
603  *
604  * @return "status" -- "000" if successful, nonzero if there is an error.
605  * @return "error" -- "" if successful, nonempty if there is an error.
606  * @return "data"."emailStatuses" -- the email statuses from the response.
607  */
608 function _ValidateAWSStatusReturn($response) {
609  try {
610  // 2e) Update status for emails that come back. (Emails that don't come back will stay in the empty not started status.)
611  if ($response == "") {
612  throw new exception ("Invalid response from script.", 11);
613  }
614  $response = HCU_JsonDecode($response);
615  if (count($response) == 0) {
616  throw new exception("Invalid response from script.", 3);
617  }
618  if (!HCU_array_key_exists("status", $response) || !HCU_array_key_exists("error", $response) || !HCU_array_key_exists("response", $response)) {
619  throw new exception("Invalid response from script.", 5);
620  }
621  if ($response["status"] !== "000") {
622  throw new exception($response["error"], 4);
623  }
624 
625  $emailStatuses = HCU_array_key_value("VerificationAttributes", $response["response"]);
626  if ($emailStatuses === false) {
627  throw new exception("Invalid response from script.", 6);
628  }
629 
630  $returnArray = array("status" => "000", "error" => "", "data" => array("emailStatuses" => $emailStatuses));
631  } catch (exception $e) {
632  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
633  }
634 
635  return $returnArray;
636 }
637 
638 /**
639  * function CheckIfEmailIsUsed($dbh, $Cu, $emailList)
640  * This function returns if the email is used in any CU emails. This determines the "used" flag which prohibits a user from removing an email.
641  *
642  * @param $dbh -- the database connection
643  * @param $Cu -- the credit union
644  * @param $emailList -- a list of emails to check
645  *
646  * @return "status" -- "000" if successful, nonzero otherwise.
647  * @return "error" -- The error message returned with the status code.
648  * @return "usedEmailList" -- A list of emails and if they are used in the CU emails or not.
649  */
650 function CheckIfEmailIsUsed($dbh, $Cu, $emailList) {
651  $usedEmailList = array();
652  try {
653  if (count($emailList) > 0) {
654  $results = _GetInvalidRoles(GetEmailNotifyRoles());
655  if ($results ["status"] !== "000") {
656  throw new exception ($results ["error"], 2);
657  }
658  extract($results["data"]);
659 
660  if (count($invalidRoles) > 0) {
661  $sql = "select distinct a.email, b.email is not null or e.tmpl_id is not null as used
662  from (values ('" . implode("'), ('", $emailList) . "')) as a (email)
663  left join (select regexp_split_to_table ( trim ( email ), E'\\\\s*;\\\\s*' ) from cuadmnotify
664  where cu = '" . prep_save($Cu, 10) . "' and role not in ('" . implode("', '", $invalidRoles) . "')) as b (email)
665  on trim(lower(a.email)) = trim(lower(b.email))
666  left join cuadmemailtmpl e on trim(lower(a.email)) = trim(lower(e.tmpl_email)) and e.cu = '" . prep_save($Cu, 10) . "'";
667 
668  $sth = db_query($sql, $dbh);
669  if (!$sth) {
670  throw new exception ("Used query failed.", 1);
671  }
672 
673  $results = _TransformUsedEmailList(db_fetch_all($sth));
674  if ($results ["status"] !== "000") {
675  throw new exception ($results ["error"], 2);
676  }
677  extract($results["data"]);
678  }
679  }
680 
681  $returnArray = array("status" => "000", "error" => "", "usedEmailList" => $usedEmailList);
682  } catch (exception $e) {
683  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
684  }
685 
686  return $returnArray;
687 }
688 
689 /**
690  * function _TransformUsedEmailList($usedEmailData)
691  * This applies the usedEmailData to the kendoGrid data.
692  *
693  * @param $usedEmailData -- the data from the CheckIfEmailIsUsed query.
694  *
695  * @return "status" -- "000" if successful, nonzero if there is an error.
696  * @return "error" -- "" if successful, nonempty if there is an error.
697  * @return "data"."usedEmailList" -- A map of emails to return.
698  */
699 function _TransformUsedEmailList($usedEmailData) {
700  $usedEmailList = array();
701  try {
702  if ($usedEmailData) {
703  foreach($usedEmailData as $row) {
704  $email = trim($row["email"]);
705  $emailKey = strtolower($email); // Emails are case insensitive.
706  $used = trim($row["used"]) == "t";
707  $usedEmailList[$emailKey] = array("email" => $emailKey, "used" => $used);
708  }
709  }
710 
711  $returnArray = array("status" => "000", "error" => "", "data" => array("usedEmailList" => $usedEmailList));
712  } catch (exception $e) {
713  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
714  }
715  return $returnArray;
716 }
717 
718 /**
719  * function _GetInvalidRoles($roles)
720  * This gets a list of invalidRoles from all the roles. Invalid roles are ones with the "hidden" attribute set to true or if is isn't "single".
721  *
722  * @param $roles -- all the roles.
723  *
724  * @return "status" -- "000" if successful, nonzero if there is an error.
725  * @return "error" -- "" if successful, nonempty if there is an error.
726  * @return "data"."invalidRoles" -- a list of invalid roles.
727  */
728 function _GetInvalidRoles($roles) {
729  $invalidRoles = array();
730  try {
731  $invalidRoles = array();
732  foreach($roles as $role => $roleRow) {
733  if (HCU_array_key_value("hidden", $roleRow) || !HCU_array_key_value("single", $roleRow)) {
734  $invalidRoles [] = prep_save($role, 15);
735  }
736  }
737 
738  $returnArray = array("status" => "000", "error" => "", "data" => array("invalidRoles" => $invalidRoles));
739  } catch (exception $e) {
740  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
741  }
742  return $returnArray;
743 }
744 
745 /**
746  * function ReadEmailNotify($dbh, $Cu)
747  *
748  * Gets the data for the kendo grid. This is driven off of the emailNotifyRoles array and uses that order.
749  *
750  * @param integer $dbh The database handle
751  * @param string $Cu The Credit Union
752  *
753  * @return array $returnArray array("code" => numeric string, "errors" => array, "data" => array)
754  * "code" is dependent on what errors there are.
755  * "errors" is a list of errors (string)
756  * "data" is the data needed for the Kendo Grid. It looks like [{roleId: string, roleText: string, required: boolean, single: boolean, email: single}]
757  */
758 function ReadEmailNotify($dbh, $Cu) {
759  $gridData = array();
760  $approvedEmails = array();
761  try {
762 
763  $roles = GetEmailNotifyRoles();
764  $sql = "select role, email from cuadmnotify where cu = '" . prep_save($Cu, 10) . "'";
765  $sth = db_query($sql, $dbh);
766  if (!$sth) {
767  throw new exception ("Role query failed.", 1);
768  }
769  $roleData = db_fetch_all($sth);
770  $featureList = FetchMenuFeatureList( array("dbh" => $dbh), array("Cu" => $Cu) );
771 
772  $records = array();
773  $results = _TransformReadEmailNotify($roles, $roleData, $featureList);
774  if ($results ["status"] !== "000") {
775  throw new exception ($results ["error"], 2);
776  }
777  extract($results["data"]);
778 
779  usort($records, "SortEmailNotify");
780 
781  $results = RetrieveApprovedEmails($dbh, $Cu);
782  if ($results["status"] !== "000") {
783  throw new exception ("Verified query failed.", 3);
784  }
785  $approvedEmails = $results["approvedEmails"];
786 
787  $returnArray = array("status" => "000", "error" => "", "data" => array("gridData" => $records, "approvedEmails" => $approvedEmails),
788  "action" => "read");
789  } catch (exception $e) {
790  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "action" => "read",
791  "data" => array("gridData" => array(), "approvedEmails" => array()));
792  }
793  return $returnArray;
794 }
795 
796 /**
797  * function RetrieveApprovedEmails($dbh, $Cu)
798  * Retrieve the approved emails from the database.
799  *
800  * @param $dbh -- the database connection
801  * @param $Cu -- the credit union
802  *
803  * @return $status -- "000" if successful, nonzero otherwise
804  * @return $error -- "" if successful, nonempty otherwise
805  * @return $approvedEmails -- A list of approved emails
806  */
807 function RetrieveApprovedEmails($dbh, $Cu) {
808  try {
809  $sql = "select trim(r.email) as email from json_to_recordset((select a.email from cuadmnotify a where a.role = 'SES'
810  and a.cu = '" . prep_save($Cu, 10) . "')::json)
811  as r (email varchar, verified boolean) where r.verified = true order by 1";
812  $sth = db_query($sql, $dbh);
813  if (!$sth) {
814  throw new exception ("Verified query failed.", 3);
815  }
816  $approvedEmails = db_fetch_all($sth);
817  $approvedEmails = $approvedEmails === false ? array() : $approvedEmails;
818 
819  // Add temporary email to end of list.
820  $approvedEmails[] = array("email" => GetTemporaryEmail());
821 
822  $returnArray = array("status" => "000", "error" => "", "approvedEmails" => $approvedEmails);
823  } catch (exception $e) {
824  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
825  }
826  return $returnArray;
827 }
828 
829 /**
830  * function SortEmailNotify($a, $b)
831  * Helper function for usort. Sorts the single address emails before the other ones so the CU notification grid shows before the other.
832  *
833  * @param $a -- the first item
834  * @param $b -- the second item
835  * @return 1 if the first item is greater than the second, 0 if they are equal (same order), -1 if the first item is less than the second.
836  */
837 function SortEmailNotify($a, $b) {
838  $cmp = $a["single"] ? ($b["single"] ? 0 : -1) : ($b["single"] ? 1 : 0);
839  return $cmp;
840 }
841 
842 /**
843  * function _TransformReadEmailNotify($roles, $roleData, $featureList)
844  * Translates the data from the query into what the kendoGrid needs.
845  *
846  * @param $roles -- all the roles.
847  * @param $roleData -- the data from the cuadmnotify table for the CU.
848  * @param $featureList -- the feature list of the CU.
849  *
850  * @return "status" -- "000" if successful, nonzero if there is an error.
851  * @return "error" -- "" if successful, nonempty if there is an error.
852  * @return "data"."records" -- the records for the kendoGrid.
853  */
854 function _TransformReadEmailNotify($roles, $roleData, $featureList) {
855  $emailMap = array();
856  $records = array();
857  try {
858  // the new email notifications are for commercial use only so do not show ACH Notifications role.
859  // use the feature menu to determine this fact.
860  $featureMenuHasCommercial = false;
861  if (in_array("ACHPMT", $featureList['data']) || in_array("ACHCOL", $featureList['data'])) {
862  $featureMenuHasCommercial = true;
863  }
864 
865  foreach($roleData as $row) {
866  $emailMap[trim($row["role"])] = trim($row["email"]);
867  }
868 
869  foreach($roles as $roleKey => $roleArray) {
870  // if no commercial features are found for the credit union menu, skip the entry
871  if (!$featureMenuHasCommercial && $roleKey == "achnotify") {
872  continue;
873  }
874 
875  if (isset($roleArray["hidden"]) && $roleArray["hidden"]) {
876  continue;
877  }
878  $email = HCU_array_key_value($roleKey, $emailMap);
879  $email = $email === false ? "" : trim($email);
880  $clear = HCU_array_key_value("clear", $roleArray);
881  $single = HCU_array_key_value("single", $roleArray);
882  $record = array("roleId" => $roleKey, "roleText" => $roleArray["desc"], "required" => !$clear && $email != "", "single" => $single, "email" => $email);
883  $records[] = $record;
884  }
885 
886  $returnArray = array("status" => "000", "error" => "", "data" => array("records" => $records));
887  } catch (exception $e) {
888  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
889  }
890  return $returnArray;
891 }
892 
893 
894 /**
895  * function GetEmailNotifyRoles()
896  *
897  * @return array $returnArray array(role => array("desc" => string, "single" => boolean, "clear" => boolean, "hidden" => boolean))
898  * "desc" will be what shows up in the description in the grid.
899  * "single", "clear", and "hidden" do not have to be set. If they aren't, then they are false.
900  * "single" specifies that there can only be one email address. If there are multiple, then an error is shown.
901  * "clear" specifies that the email address can be removed. Normally email addresses cannot be removed but they can be changed.
902  * "hidden" specifies that the role doesn't appear on the page.
903  */
904 function GetEmailNotifyRoles() {
905 
906  return [
907  "upload" => ["desc" => "File Upload Confirmations"],
908  "alert" => ["desc" => "Alerts From / Returned To", "single" => true],
909  "esload" => ["desc" => "eStatement Upload"],
910  "agree" => ["desc" => "eStatement Signups / Stops"],
911  "preset" => ["desc" => "Password Reset CU Copy To"],
912  "presetfrom" => ["desc" => "Password Reset Mailed From", "single" => true],
913  "transfernotify" => ["desc" => "Transfers / Repeating Txn Failures"],
914  // 06-19 note that "onldepnotify" does not refer to admin email notifications for online
915  // deposits. This key is only used for loan app submission notifications.
916  "onldepnotify" => ["desc" => "Loan System Notifications"],
917  "securenotify" => ["desc" => "Secure Message Notification", "clear" => true],
918  "securemsgfrom" => ["desc" => "Secure Message Notification Mailed From", "single" => true],
919  "securitychgfrom" => ["desc" => "Security Settings Changed Mailed From", "single" => true],
920  "enrollnotify" => ["desc" => "Enrollment Form Notification", "clear" => true, "delete" => true],
921  "txtbanking" => ["desc" => "TXT Banking Email", "clear" => true, "hidden" => true],
922  "twtralert" => ["desc" => "Twitter Alert Account", "clear" => true, "hidden" => true],
923  "achnotify" => ["desc" => "ACH Notifications", "single" => true],
924  "hcunotice" => ["desc" => "HomeCU Special Notifications<br><font size=1>(Planned outages, special updates, etc.)</font>"],
925  "ccemail" => ["desc" => "Credit Card Payment Request", "hidden" => true],
926  // This role is for verifying emails.
927  "SES" => ["hidden" => true]
928  ];
929 }
930 
931 
932 /**
933  * function UpdateEmailNotify($dbh, $Cu, $showSQL, $role, $email)
934  *
935  * Update/insert a record for the kendo grid (depending on if it exists).
936  *
937  * @param integer $dbh The database handle
938  * @param string $Cu The Credit Union
939  * @param boolean $showSQL If true, then SQL is returned
940  * @param string $role The role to update/insert
941  * @param string $email The email value
942  * @param boolean $specialEmailUpdate If true, email is being updated through the welcome screen so use a different audit
943  *
944  * @return array $returnArray array("code" => numeric string, "errors" => array, "data" => array)
945  * "code" is dependent on what errors there are.
946  * "errors" is a list of errors (string)
947  * "data" is the data needed for the Kendo Grid. It looks like [{roleId: string, roleText: string, required: boolean, single: boolean, email: single}]
948  */
949 function UpdateEmailNotify($dbh, $Cu, $Cn, $role, $email, $specialEmailUpdate) {
950  $roles = GetEmailNotifyRoles();
951 
952  try {
953  $results = _TransformRoleForUpdate($role, $roles, $email);
954  if ($results ["status"] !== "000") {
955  throw new exception ($results ["error"], 9);
956  }
957  extract($results["data"]);
958 
959  $sql = "select 'FOUND' from cuadmnotify where cu = '" . prep_save($Cu, 10) . "' and role = '" . prep_save($role, 15) . "'";
960  $sth = db_query($sql, $dbh);
961  if (!$sth) {
962  throw new exception("notify find query failed.", 6);
963  }
964  $recordExists = db_num_rows($sth) > 0;
965 
966  $sql = "select email from cuadminusers where user_name = '$Cn' and cu = '$Cu'";
967  $sth = db_query($sql, $dbh);
968  if (!$sth) {
969  throw new exception("Email query failed.", 7);
970  }
971  $userEmail = db_fetch_row($sth)[0];
972 
973  $results = _CreateUpdateTableForUpdateAudit($Cu, $role, $returnRecord["email"], $recordExists, $userEmail, $specialEmailUpdate);
974  if ($results ["status"] !== "000") {
975  throw new exception ($results ["error"], 10);
976  }
977  extract($results["data"]);
978 
979  if (DataAdminTableUpdate($dbh, array("cu" => $Cu), $updateTable, $Cn, $shortLabel, "notificationMaintenance.prg", "A",
980  $longLabel, $Cn, $userEmail, trim($_SERVER["REMOTE_ADDR"])) === false) {
981  throw new exception("Update failed.", 8);
982  }
983 
984  $returnArray = array("status" => "000", "error" => "", "record" => array($returnRecord), "info" => "Email has been updated successfully", "action" => "update");
985  } catch(exception $e) {
986  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage(), "action" => "update");
987  }
988  return $returnArray;
989 }
990 
991 /**
992  * function _TransformRoleForUpdate($role, $roles, $email)
993  * Validates and gets a record ready for the kendoGrid.
994  *
995  * @param $role -- the role to update.
996  * @param $roles -- the list of roles.
997  * @param $email -- the email to update.
998  *
999  * @return "status" -- "000" if successful, nonzero if there is an error.
1000  * @return "error" -- "" if successful, nonempty if there is an error.
1001  * @return "data"."returnRecord" -- the updated record.
1002  */
1003 function _TransformRoleForUpdate($role, $roles, $email) {
1004  $numEmails = 0;
1005  try {
1006  $roleRecord = HCU_array_key_value($role, $roles);
1007  if ($roleRecord === false) {
1008  throw new exception("role record not found.", 1);
1009  }
1010 
1011  if (trim($email) == "" && !(isset($roleRecord["clear"]) && $roleRecord["clear"])) {
1012  throw new exception("Email cannot be cleared.", 2);
1013  }
1014 
1015  $emails = array();
1016 
1017  foreach (preg_split('/[;, ]/',$email) as $ema) {
1018  $ema = trim($ema);
1019  if ($ema != "") {
1020  $numEmails++;
1021  if (!validateEmail($ema)) {
1022  throw new exception("One of the emails is invalid.", 3);
1023  }
1024 
1025  $emails[] = strtolower(trim($ema));
1026  }
1027  }
1028 
1029  // Want to do a little bit of cleanup. Yes, "a@bcd.efg; ;, h@ijk.lmn;" is legal but set it to "a@bcd.efg;h@ijk.lmn".
1030  $email = implode(";", $emails);
1031 
1032  if ($numEmails == 0) {
1033  throw new exception("Email cannot be cleared.", 4);
1034  }
1035 
1036  if ($numEmails > 1 && isset($roleRecord["single"]) && $roleRecord["single"]) {
1037  throw new exception("Only one address is allowed.", 5);
1038  }
1039 
1040  $returnRecord = array("roleId" => $role, "roleText" => $roleRecord["desc"], "email" => $email,
1041  "required" => trim($email) != "" && !(isset($roleRecord["clear"]) && $roleRecord["clear"]), "recordExists" => "Y");
1042 
1043  $returnArray = array("status" => "000", "error" => "", "data" => array("returnRecord" => $returnRecord));
1044  } catch (exception $e) {
1045  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
1046  }
1047  return $returnArray;
1048 }
1049 
1050 /**
1051  * function _CreateUpdateTableForUpdateAudit($Cu, $role, $email, $recordExists, $userEmail)
1052  * This creates the audit for updating an email.
1053  *
1054  * @param $Cu -- the credit union
1055  * @param $role -- the role to update
1056  * @param $email -- the email to update
1057  * @param $recordExists -- does the record exist? (Create versus update audit)
1058  * @param $userEmail -- the email of the logged in user
1059  * @param $specialEmailUpdate -- if the email was updated through the welcome screen or not
1060  *
1061  * @return "status" -- "000" if successful, nonzero if there is an error.
1062  * @return "error" -- "" if successful, nonempty if there is an error.
1063  * @return "data"."updateTable" -- what to send to the audit function to make the change and audit it.
1064  */
1065 function _CreateUpdateTableForUpdateAudit($Cu, $role, $email, $recordExists, $userEmail, $specialEmailUpdate) {
1066  $shortLabel = "";
1067  $longLabel = "";
1068  try {
1069 
1070  $updateTable = array("cu" => $Cu, "role" => $role, "email" => $email);
1071  if ($recordExists) {
1072  $updateTable["_action"] = "update";
1073 
1074  if ($specialEmailUpdate) {
1075  $shortLabel = "UPD_SNOTI";
1076  $longLabel = "Update Special Email Notification";
1077  } else {
1078  $shortLabel = "UPD_NOTI";
1079  $longLabel = "Update Notification Emails";
1080  }
1081  } else {
1082  $updateTable["_action"] = "insert";
1083 
1084  if ($specialEmailUpdate) {
1085  $shortLabel = "ADD_SNOTI";
1086  $longLabel = "Add Special Email Notification";
1087  } else {
1088  $shortLabel = "ADD_NOTI";
1089  $longLabel = "Add Notification Emails";
1090  }
1091  }
1092 
1093  $updateTable = array("cuadmnotify" => array($updateTable));
1094 
1095  $returnArray = array("status" => "000", "error" => "", "data" => array("updateTable" => $updateTable, "shortLabel" => $shortLabel, "longLabel" => $longLabel));
1096  } catch (exception $e) {
1097  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
1098  }
1099  return $returnArray;
1100 }
1101 
1102 /**
1103  * function ResendEmail($dbh, $Cu, $email)
1104  * Resends one email that was sent more than 24 hours ago.
1105  *
1106  * @param $dbh -- the database connection
1107  * @param $Cu -- the credit union
1108  * @param $email -- the email to resend
1109  *
1110  * @return "status" -- "000" if successful, nonzero if there is an error.
1111  * @return "error" -- "" if successful, nonempty if there is an error.
1112  * @return "email" -- the email that was resent. This is to update the manage email list grid.
1113  * @return "info" -- the information message to send.
1114  */
1115 function ResendEmail($dbh, $Cu, $email) {
1116  try {
1117 
1118  // Step 1: Validate email.
1119  if (!validateEmail($email)) {
1120  throw new exception("Email is invalid.", 1);
1121  }
1122 
1123  $email = trim(strtolower($email));
1124 
1125  $encoded = trim(str_replace('"', '\x22', $email));
1126  // escapeshellarg() does too much. It will escape all the quotes in the command.
1127 
1128  // Step 2: Call python script to resend email.
1129  $pyFile = dirname(__FILE__) . "/../../shared/scripts/verifySESEmail.py";
1130  $pyAction = "verifyEmails";
1131  $cmd = "python3 $pyFile -a $pyAction -e " . '"' . $encoded . '"';
1132  $response = HCU_JsonDecode(exec($cmd));
1133  if ($response["status"] !== "000") {
1134  throw new exception ($response["error"], 2);
1135  }
1136 
1137  // Step 3: Get SES role from database.
1138  $sql = "select email from cuadmnotify where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
1139  $sth = db_query($sql, $dbh);
1140  if (!$sth) {
1141  throw new exception ("Select query failed.", 3);
1142  }
1143 
1144  $emailArray = db_num_rows($sth) > 0 ? db_fetch_row($sth, 0)[0] : "[]";
1145  $emailArray = HCU_JsonDecode($emailArray);
1146 
1147  // Step 4: Update SES role with "date" of now for the resent email.
1148  foreach($emailArray as $i => $emailRow) {
1149  $checkEmail = HCU_array_key_value("email", $emailRow);
1150  $checkEmail = $checkEmail === false ? "" : trim(strtolower($checkEmail));
1151 
1152  $newEmailRow = $emailRow;
1153  $newEmailRow["email"] = $checkEmail;
1154  if ($checkEmail == $email) {
1155  $date = new DateTime("now", new DateTimeZone("UTC"));
1156  $date = $date->format("U");
1157  $newEmailRow["date"] = intval($date);
1158  }
1159 
1160  $emailArray[$i] = $newEmailRow;
1161  }
1162 
1163  $emailArray = HCU_JsonEncode($emailArray);
1164 
1165  $sql = "update cuadmnotify set email = '" . prep_save($emailArray) . "'
1166  where cu = '" . prep_save($Cu, 10) . "' and role = 'SES'";
1167  $sth = db_query($sql, $dbh);
1168  if (!$sth) {
1169  throw new exception ("Update query failed.", 4);
1170  }
1171 
1172  $returnArray = array("status" => "000", "error" => "", "email" => $email, "info" => "Email was resent to $email.");
1173  } catch (exception $e) {
1174  $returnArray = array("status" => $e->getCode(), "error" => $e->getMessage());
1175  }
1176  return $returnArray;
1177 }