Odyssey
hcuAppFeed.prg
1 <?php
2 # NOTE 10/09/12 exposed logic bug -- cu set as 2-factor, app passing in MFA answers, then cu set as legacy: appeared to work except that USERKEY was not returned in OFX output. Some kind of wart related to getting MFA when not expected
3 # Automatic 1-Way OFX as described in OFX 2.1.1 Specification, Section 16
4 #
5 #
6 # APPID : Identifies partner. Use OFXRQ for HomeCU app
7 # APPVER : required according to spec, not used at HomeCU.
8 # ORG : Client Code
9 #
10 # Script supports both SSO access and authenticated access methods.
11 # request will include either USERKEY (indicating SSO)
12 # or USERID/USERPASS (indicating authenticated access)
13 #
14 # USERID : cu:member
15 # USERPASS : password for authenticated account
16 # USERKEY : token for SSO access (replaces both USERID and USERPASS)
17 # CRED2 : Request date, UTC date/time in YYYYmmddHHMISS format
18 # CRED3 : hash # APPID USERID CRED2 hashed with assigned key
19 # DTSTART : Start date, default to 30days past
20 # for Odyssey default to yesterday
21 # DTEND : End date, default to today
22 #
23 # NOTE 01/25/18 since AppFeed is specific to Apps we are going to remove need for
24 # CRED2 and CRED3. Don't check at login, and the cookie check will validate
25 # after authentication.
26 define( "MAX_PHONES", 5 );
27 
28 // ** SET HOMECU FLAGS
29  $serviceMinimal = true;
30  $serviceShowInfo = false;
31  $serviceLoadMenu = false;
32  $serviceShowMenu = false;
33 
34  // ** INCLUDE MAIN GLOBAL SCRIPT -- Handles security / global variable values
35  // hcuService will be returning a status object: e.g. ["homecuErrors":{[{"message":"message1"}...{"message":"messageN"}}]
36  require_once(dirname(__FILE__) . '/../library/hcuService.i');
37  require_once(dirname(__FILE__) . '/../../shared/library/cutrusted.i');
38  require_once(dirname(__FILE__) . '/../../shared/library/hcuAppCommon.i');
39  require_once(dirname(__FILE__) . '/../library/cu_credentials.i');
40  require_once(dirname(__FILE__) . '/../../shared/library/cu_sms.i');
41  require_once(dirname(__FILE__) . '/../library/permissions.i');
42  require_once(dirname(__FILE__) . '/../library/hcuTransfer.i');
43  require_once(dirname(__FILE__) . '/../library/hcuTransferScheduled.i');
44  require_once('LogSSO.i');
45 
46  $inPost = array();
47  $varOk = array(
48  "APPID" => array('filter' => FILTER_SANITIZE_STRING),
49  "DTSTART" => array('filter' => FILTER_SANITIZE_STRING),
50  "DTEND" => array('filter' => FILTER_SANITIZE_STRING),
51  "CRED2" => array('filter' => FILTER_SANITIZE_STRING),
52  "CRED3" => array('filter' => FILTER_SANITIZE_STRING),
53  "USERID" => array('filter' => FILTER_SANITIZE_STRING),
54  "USERPASS" => array('filter' => FILTER_SANITIZE_STRING),
55  "USERKEY" => array('filter' => FILTER_SANITIZE_STRING),
56  "MFAKEY" => array('filter' => FILTER_SANITIZE_STRING),
57  "DEVICE_COOKIE" => array('filter' => FILTER_SANITIZE_STRING),
58  "MFABUNDLE" => array('filter' => FILTER_SANITIZE_STRING),
59  "ORG" => array('filter' => FILTER_SANITIZE_STRING),
60  "RQMODE" => array('filter' => FILTER_SANITIZE_STRING),
61  "FACCTID" => array('filter' => FILTER_SANITIZE_STRING),
62  "TACCTID" => array('filter' => FILTER_SANITIZE_STRING),
63  "AMOUNT" => array('filter' => FILTER_SANITIZE_NUMBER_FLOAT, 'options' => array( "flags" => FILTER_FLAG_ALLOW_FRACTION ) ),
64  "TRMEMO" => array('filter' => FILTER_SANITIZE_STRING),
65  "PASSTO" => array('filter' => FILTER_SANITIZE_STRING),
66  "PASSWITH" => array('filter' => FILTER_SANITIZE_STRING),
67  "KEYACCTID" => array('filter' => FILTER_SANITIZE_STRING),
68  "CFGFLAG" => array('filter' => FILTER_SANITIZE_STRING),
69  "UPDAWARE" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
70  "USERMENU" => array('filter' => FILTER_SANITIZE_STRING),
71  "SENDAS" => array('filter' => FILTER_SANITIZE_STRING),
72  "AUTHMODE" => array('filter' => FILTER_SANITIZE_STRING),
73  'MFA_' => 'prefix_s',
74  'MFS_' => 'prefix_s'
75  );
76 # CFGFLAG is to be used to capture the session flag settings as used in Fmsg_tx on the desktop. Should be used
77 # currently in TXLIST / TXPOST to control whether or not to query the core for txaccount authority. Not implemented
78 # completely yet, but added to the Load_HB_ENV function and calls
79 
80 HCU_ImportVars( $inPost, "", $varOk );
81 # apptokenkey used only at HCU, not shared. Used to hash / check our token just to be sure it comes back unchanged.
82 $apptokenkey = 'Chahriv8pahvahsa';
83 $encoding='UTF-8';
84 
85 // This is a global for holding the userkey because each new userkey builds off the old one.
86 // It is analagous to a cookie.
87 $gSavedUserkey = "";
88 
89 try {
90 # FIX FOR PROD
91 # must be POST method
92 if ($_SERVER['REQUEST_METHOD'] != 'POST')
93  throw new Exception('Unauthorized Method',2010);# post method required
94 
95 function trim_item(&$item, $key) {
96  $item = trim($item);
97 }
98 
99 array_walk_recursive($inPost, 'trim_item');
100 
101 //$inPost["APPID"] = trim( $inPost["APPID"] );
102 switch ($inPost['APPID']) {
103  case "OFXRQ":
104  # iPhone APP
105  $client_source_override = 'APP';
106  # no ip whitelist for iPhone app
107  $appkey = "yuiQu8laLux7bahx";
108  # require a minimum version to continue
109  $appminver = 1;
110  $appdataver = 1;
111  break;
112 
113  case "OFXAA":
114  # Android APP
115  $client_source_override = 'ADA';
116  # no ip whitelist for Android app
117  $appkey = "Gai7Vain2pab5zae";
118 
119  # require a minimum version to continue
120  $appminver = 1;
121  $appdataver = 1;
122  break;
123 
124  default:
125  $HB_ENV['SYSENV']['logger']->info('request: ' . HCU_JsonEncode($_REQUEST));
126  $HB_ENV['SYSENV']['logger']->info('inPost: ' . HCU_JsonEncode($inPost));
127  throw new Exception("Unauthorized ID",2020); # invalid app id
128 }
129 # validate entries
130 # start date optional, must be valid if provided, default 30 days past
131 # end date optional, but must be valid if provided, default today
132 # either USERID/USERPASS or USERKEY is required
133 # USERID HomeCU MEMBER for aggregated access
134 # USERPASS password for aggregated access
135 # USERKEY required if USERID/USERPASS not given; SSO security token
136 # CRED2 required, UTC date in YYYYMMDDHHMI format.
137 # must be within 30 minutes of actual UTC time as shown on HomeCU servers
138 # CRED3 hash required, must match calculated hash
139 #
140 # As of 01/25/18 no longer requiring CRED2 or CRED3 to act more like the desktop client.
141 
142 if ( empty($inPost['ORG']) ) {
143  throw new Exception("Invalid Credentials",2030); # Missing values
144 }
145 
146 $CU = $inPost['ORG'];
147 $SENDAS = (HCU_array_key_value('SENDAS',$inPost) && in_array(strtoupper($inPost['SENDAS']), array('JSON', 'XML')) ? $inPost['SENDAS'] : 'XML');
148 
149 if (!empty($inPost['USERKEY'])) {
150  # got USERKEY, validate SSO method
151  $AUTHMODE = 'SSO';
152  $HB_ENV['AuthMode'] = $AUTHMODE;
153 
154  # AuthMode=SSO w/Userkey
155 
156  // see if original userkey or the session ticket
157  $apptokarr = array();
158  parse_str(urldecode($inPost['USERKEY']), $apptokarr);
159 
160  // see if original UserKey
161  if ( isset( $apptokarr["E"] ) && isset( $apptokarr["H"] ) ) {
162  $keycheck = CheckUserkey($inPost['ORG'], $inPost['USERKEY'], $apptokenkey);
163 
164 // # if no C= value, assume oldstyle userkey
165 // # and set member as A= value so hash works
166 // # for odyssey A & C are swapped
167 // # A is Uid, C is primary account
168 // # Get_History call uses cauth as primary account
169 
170  # don't want to need this block -
171  # if needed, return CAUTH, MEMBER, EXPIRES from CheckUserkey
172 // if (is_null($apptokarr['C'])) {
173 // $CAUTH = $apptokarr['A'];
174 // } else {
175 // $CAUTH = $apptokarr['C'];
176 // }
177  $MEMBER = $apptokarr['A'];
178 
179  if ($keycheck['Status']['Message'] !== 'Success') {
180  throw new Exception($keycheck['Status']['Message'], $keycheck['Status']['Code']);
181  }
182  } else {
183  $keycheck = CheckSessionUserkey( $HB_ENV, $inPost['USERKEY'] );
184 
185  if ($keycheck['Status']['Message'] !== 'Success') {
186  throw new Exception($keycheck['Status']['Message'], $keycheck['Status']['Code']);
187  }
188 
189  $MEMBER = $keycheck["data"]["Cn"];
190 
191  // save the userkey for future updates
192  $gSavedUserkey = $inPost['USERKEY'];
193  }
194 
195 } else {
196  # No USERKEY, default to aggregate AUTH method
197  # for Odyssey, USERID will represent a user_name (login id) value
198  $AUTHMODE = (empty($inPost['AUTHMODE']) ? "MFA" : $inPost['AUTHMODE']);
199  $HB_ENV['AuthMode'] = $AUTHMODE;
200 
201 // if (empty($inPost['USERID']) || strlen(trim($inPost['USERPASS'])) < 4) {
202  if ( empty(HCU_array_key_value('USERID',$inPost)) ) {
203  throw new Exception("Invalid Credentials",2040); #MFQ missing ID
204  }
205  $MEMBER = $inPost['USERID'];
206 }
207 
208 if (!empty($MEMBER) && (preg_match("/[\\\`,\"\s;]/", $MEMBER))) {
209  throw new Exception("Invalid Credentials",2041); #Member Bad Characters
210 }
211 
212 # set the basics so we can get the rest...
213 $HB_ENV['Cu'] = $inPost['ORG'];
214 $cu = $inPost['ORG'];
215 $SENDAS = (!empty($inPost['SENDAS']) && in_array(strtoupper($inPost['SENDAS']), array('JSON', 'XML')) ? $inPost['SENDAS'] : 'XML');
216 
217 #replaced with call to LoadCuAdmin() in the top of hcuService.i
218 // Check_HomeCU_Status($dbh, $HB_ENV);
219 
220 $HB_ENV['platform'] = $client_source_override;
221 $CFGFLAG= HCU_array_key_value('CFGFLAG', $inPost);
222 /*
223  * this line prints the HB_ENV array minus the MC dictionary
224  *
225  // $HB_ENV["SYSENV"]["logger"]->info( "\n\n" . print_r(array_diff_key($HB_ENV,array('MC' => 'drop')),true) . "\n\n" );
226  */
227 
228 Load_HB_ENV($dbh, $inPost['ORG'], $MEMBER, $HB_ENV, $CFGFLAG);
229 
230 if (HCU_array_key_exists('USERKEY', $inPost)) {
231  Check_Member_Credentials($HB_ENV, $inPost['USERKEY']);
232 }
233 
234 # set these things so they are available from the Global scope for
235 # throtlpkt functions
236 $MEMBER=$HB_ENV['Cn'];
237 $Cu = $HB_ENV['Cu'];
238 $Cn = $HB_ENV['Cn'];
239 $CAUTH = 0;
240 $Clw = $HB_ENV['livewait'];
241 $Ml = urldecode($HB_ENV['Ml']);
242 
243 $HB_ENV['allowReadonly'] = true;
244 if (!hcu_checkOffline($dbh, $HB_ENV)) {
245  throw new Exception($HB_ENV['offlineblurb'],15520); # CU is marked offline
246  exit;
247 }
248 if (empty($HB_ENV['Cn'])) {
249 // unset($HB_ENV['MC']);
250 // $HB_ENV["SYSENV"]["logger"]->info("Result:\n" . print_r($HB_ENV,true) );
251 
252  throw new Exception('Authentication Failed',2003); # invalid Cn
253 }
254 if (empty($HB_ENV['Cu'])) {
255  throw new Exception('Authentication Failed',2002); # invalid Cu
256 }
257 
258  // initialize this because it is only returned on some paths (i.e. right at sign-on)
259  $appEncryptionKey = "";
260 
261 // Now supporting setting credentials from app,
262 // so 'Last Chance Login' no longer applies
263 // leave block, as this might be how we present a billboard for outdated apps
264 //if ($inPost['UPDAWARE'] == 0 && ($HB_ENV['Ffchg'] == 'Y' || $HB_ENV['Ffreset'] > 0) && $HB_ENV['Ffremain'] == 1) {
265 // throw new Exception('Member Last Chance Login - Please log in through the full Home Banking site to reset security credentials.',15503);
266 //}
267  // With Pegaus the credential update / setting is going to be done with ProfileRequire. To make sure
268  // a 3100 code is not returned, the apps should set UPDAWARE=2 for Login and MFA process.
269 if ($AUTHMODE !== "SSO") {
270  # now that the HB_ENV array is filled, finish authentication if MFA
271 
272  # Why yes, this IS hitting the database a second time to read the same member
273  # info we just got for Load_HB_ENV. But by migrating this way and using same
274  # functions as desktop to validate, in the future
275  # we can use this instead and float closer to the desktop codeset
276 
277  $userrec = GetUserbyName($dbh, $inPost['ORG'], $MEMBER);
278 
279  if ( $userrec['lockedacct'] ) {
280  throw new Exception('Account Locked', 15502); # locked
281  }
282 
283  // at this point we can add more information to the HB_ENV environment
284  // NOTE: this functionality mimics BankingVerifyCredentials
285  $HB_ENV['Ce'] = time() + $HB_ENV['SYSENV']['ticket']['expires'];
286  $HB_ENV['Clu'] = (empty($userrec['lastupdate']) ? $MC->msg("Unknown") : urlencode(trim($userrec['lastupdate'])));
287  $HB_ENV['Fhdays'] = $userrec['fhdays'];
288 
289  // Set the session id. This will be run only once.
290  $HB_ENV["sid"] = strval(time());
291 
292  // Initialize the saved user cookie if it hasn't been set yet (now that HB_ENV is complete).
293  if ( empty( $gSavedUserkey ) ) {
294  $gSavedUserkey = BuildBaseSessionTicket( $HB_ENV );
295  }
296 
297  switch ($HB_ENV['AuthMode']) {
298  case 'MFA':
299  // expect USERID, optionally tolerate USERPASS and (MFAKey or DEVICE_COOKIE)
300  // DEVICE_COOKIE will be in the form name|content|expire
301  #
302  # CHECK USERID / USERPASS if given, even if MFAKEY is not given
303  # IF EITHER ONE FAILS, SEND HARD FAIL
304  #
305  # USERID validated by userrec above
306 
307  if (HCU_array_key_exists('USERID', $inPost) && HCU_array_key_exists('USERPASS', $inPost)) {
308  if ($userrec['rowfound'] == 0 ||
309  !password_verify($inPost['USERPASS'], $userrec['passwd']) ||
310  strlen(trim($inPost['USERPASS'])) < 4) {
311  // unset($HB_ENV['MC']);
312  // $HB_ENV["SYSENV"]["logger"]->info("inPost:\n" . print_r($inPost,true) . "\nUserrec:\n" . print_r($userrec,true) );
313 
314  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_PWD']);
315 
316  # this should log password failure if rowfound == 1
317  throw new Exception('Invalid Username or Password', 15501); # userrec not found or passwd fails
318  }
319 
320  // At this point the user is authenticated but the next check might cause a response to be return
321  // instead of falling through.
322  }
323  # If no USERPASS or no MFAKEY or invalid MFAKEY or Member forced to reset, send email prompt
324  // NOTE: Currently (10/10/19) allowing an MFAKEY or a DEVICE_COOKIE
325  $sendEmailResponse = true;
326  if ( HCU_array_key_exists('USERPASS', $inPost) ||
327  $HB_ENV['Ffreset'] != GetUserFlagsValue('MEM_FORCE_RESET') ) {
328  // we are just testing the existence of the USERPASS; the actual value is tested later
329 
330  // test the (newer) device cookie first
331  if ( HCU_array_key_exists('DEVICE_COOKIE', $inPost) &&
332  IsValidAppDeviceCookie($HB_ENV["cu"], HCU_array_key_value('DEVICE_COOKIE', $inPost), $userrec) ) {
333  // all tests pass
334  $sendEmailResponse = false;
335  } else if ( HCU_array_key_exists('MFAKEY', $inPost) &&
336  IsValidMFAKey(HCU_array_key_value('MFAKEY', $inPost), $userrec) ) {
337  // all tests pass
338  $sendEmailResponse = false;
339  }
340  }
341 
342  if ( $sendEmailResponse ) {
343  # first step, ignore any previous bundle
344  # build empty bundle with USERID,
345  # add USERPASS and MFAKEY if given
346  $mfaBundle = array('mode' => 'MFA', 'USERID' => $inPost['USERID']);
347  if (HCU_array_key_exists('USERPASS', $inPost)) {
348  $mfaBundle['USERPASS'] = $inPost['USERPASS'];
349  }
350  if (HCU_array_key_exists('MFAKEY', $inPost) && $HB_ENV['Ffreset'] != GetUserFlagsValue('MEM_FORCE_RESET')) {
351  $mfaBundle['MFAKEY'] = $inPost['MFAKEY'];
352  }
353  $mfaBundle['BUILDTIME'] = time();
354  $mfaBundle = createBundle($inPost['ORG'], $mfaBundle);
355  if (empty($mfaBundle)) {
356  throw new Exception("Authentication Failed Building Credential Package", 15597); # bundle failed
357  }
358  $HB_ENV['mfaBundle'] = $mfaBundle;
359  $reply_arr = EML_prompt($dbh, $HB_ENV);
360 
361  // this exits
362  send_response( $reply_arr, $SENDAS );
363  }
364  break;
365  case 'EML':
366  # expect USERID, MFA_E (EMAIL)
367  # validate Email
368  $mfaBundle = HCU_array_key_value('MFABUNDLE', $inPost);
369  if (empty($mfaBundle)) {
370  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
371  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
372  }
373  $mfaBundle = openBundle($inPost['ORG'], $mfaBundle);
374  if (!checkBundle('EML', $mfaBundle, $inPost)) {
375  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
376  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
377  }
378  if (!checkBundle('TIME', $mfaBundle, $inPost)) {
379  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL_TIMEOUT']);
380  throw new Exception("Authentication Failed Login Process Took Too Long", 15595); # bundle expired
381  }
382  # isValidEmail checks for db empty and valid input but... somebody should save this....
383  if (!HCU_array_key_value('MFA_E', $inPost) || !isValidEmail(HCU_array_key_value('MFA_E', $inPost), $userrec)) {
384  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_EMAIL']);
385  throw new Exception("Authentication Failed Invalid Email", 15506); # email
386  } else {
387  $inBundle = $mfaBundle; # might test password again, so save the incoming bundle before we recalculate it
388  $mfaBundle['mode'] = 'EML';
389  $mfaBundle['MFA_E'] = $inPost['MFA_E'];
390  $mfaBundle['BUILDTIME'] = time();
391  $mfaBundle = createBundle($inPost['ORG'], $mfaBundle);
392  if (empty($mfaBundle)) {
393  throw new Exception("Authentication Failed Building Credential Package", 15597); # bundle failed
394  }
395  $HB_ENV['mfaBundle'] = $mfaBundle;
396  # if sec reset, prompt for password, else send challenge
397  if ($HB_ENV['Ffreset'] == GetUserFlagsValue('MEM_FORCE_RESET')) {
398  # if force security reset, skip the MFA steps.
399  # If we have a password and it is valid, fall through as logged in
400  if (!HCU_array_key_value('USERPASS', $inBundle) || !password_verify($inBundle['USERPASS'], $userrec['passwd']) ||
401  strlen(trim($inBundle['USERPASS']) < 4)) {
402  $reply_arr = PWD_prompt($dbh, $HB_ENV);
403  // this exits
404  send_response( $reply_arr, $SENDAS );
405  }
406  } else {
407 
408  if (intval($userrec['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE'))) {
409  # using secure access code
410  # send destination list
411  if (trim($userrec['email']) == '') {
412  $HB_ENV['MFA_E'] = $inPost['MFA_E'];
413  }
414  $reply_arr = SAC_destination($dbh, $HB_ENV, $userrec);
415  } else {
416  # using challenge questions
417  $reply_arr = MFQ_send_chall($dbh, $HB_ENV, $MC);
418  }
419 
420  // this exits
421  send_response( $reply_arr, $SENDAS );
422  }
423  }
424  break;
425  case 'MFQ':
426  # expect USERID, MFA responses as in current challenge questions
427  # if valid MFA response check password (send password request?)
428  $mfaBundle = HCU_array_key_value('MFABUNDLE', $inPost);
429  if (empty($mfaBundle)) {
430  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
431  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
432  }
433  $mfaBundle = openBundle($inPost['ORG'], $mfaBundle);
434  if (!checkBundle('MFQ', $mfaBundle, $inPost)) {
435  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
436  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
437  }
438  if (!checkBundle('TIME', $mfaBundle, $inPost)) {
439  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL_TIMEOUT']);
440  throw new Exception("Authentication Failed Login Process Took Too Long", 15595); # bundle expired
441  }
442  if (!(MFQ_defined($inPost) > 0)) {
443  throw new Exception('Missing MFQ Parameters', 2040);
444  } else {
445  # MFQ_defined($inPost) > 0)
446  # if (2-factor & not security reset) & MFA_* exists, we have MFA response, validate answers
447  list($fail, $failreason) = MFQ_response($dbh, $HB_ENV, $inPost);
448  if ($fail == 0) {
449  # challenge succeeded, ask for password
450  $mfaBundle['mode'] = 'MFQ';
451  $mfaBundle['haveMFQ'] = 'MFQ';
452  $mfaBundle['BUILDTIME'] = time();
453  $mfaBundle = createBundle($inPost['ORG'], $mfaBundle);
454  if (empty($mfaBundle)) {
455  throw new Exception("Authentication Failed Building Credential Package", 15597); # bundle failed
456  }
457  $HB_ENV['mfaBundle'] = $mfaBundle;
458  $reply_arr = PWD_prompt($dbh, $HB_ENV);
459  // this exits
460  send_response( $reply_arr, $SENDAS );
461  } else {
462  LogFail($dbh, $HB_ENV, $inPost, $failreason, $GLOBALS['MEM_LOGIN_FAILED_QST']);
463  # should this be a fail instead? W/new method,
464  # only get MFQ response if I asked for it
465 // $reply_arr = MFQ_send_chall($dbh, $HB_ENV, $MC);
466  throw new Exception("Authentication Failed Invalid Challenge", 15506); # email
467  }
468  }
469  break;
470  case 'MFS':
471  # expect USERID, MFS_Deliver (destination for SAC)
472  # if valid destination & email send SAC
473  $mfaBundle = HCU_array_key_value('MFABUNDLE', $inPost);
474  if (empty($mfaBundle)) {
475  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
476  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
477  }
478  $mfaBundle = openBundle($inPost['ORG'], $mfaBundle);
479  if (!checkBundle('MFS', $mfaBundle, $inPost)) {
480  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
481  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
482  }
483  if (!checkBundle('TIME', $mfaBundle, $inPost)) {
484  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL_TIMEOUT']);
485  throw new Exception("Authentication Failed Login Process Took Too Long", 15595); # bundle expired
486  }
487  if (!HCU_array_key_exists('MFS_Deliver', $inPost)) {
488  throw new Exception('Missing Parameter Delivery Method', 2040);
489  }
490 
491  // * generate a new code
492  # might need to get Cu & lookup length + ttl
493  # but right now just use defaults 6 and 1200
494  $authResp = generateAuthcode();
495  if (!$authResp['authcode'] || !$authResp['authexpires']) {
496  throw new Exception('Generate Access Code Failed', 3050);
497  }
498  if (!setAuthcode($dbh, $HB_ENV, $HB_ENV['MC'], $userrec, $authResp['authcode'], $authResp['authexpires'])) {
499  throw new Exception('Save Access Code Failed', 3050);
500  }
501  $sendto = str_replace(array("-", "_", "."), array("+", "/", "="), $inPost['MFS_Deliver']);
502  if (!sendAuthcode($HB_ENV['dbh'], $HB_ENV, $userrec, $sendto)) {
503  throw new Exception('Send Access Code Failed', 3050);
504  }
505  $mfaBundle['mode'] = 'MFS';
506  $mfaBundle['MFS_Deliver'] = $inPost['MFS_Deliver'];
507  $mfaBundle['BUILDTIME'] = time();
508  $mfaBundle = createBundle($inPost['ORG'], $mfaBundle);
509  if (empty($mfaBundle)) {
510  throw new Exception("Authentication Failed Building Credential Package", 15597); # bundle failed
511  }
512  $HB_ENV['mfaBundle'] = $mfaBundle;
513  $reply_arr = SAC_prompt($dbh, $HB_ENV);
514  // this exits
515  send_response( $reply_arr, $SENDAS );
516  break;
517  case 'MFC':
518  $mfaBundle = HCU_array_key_value('MFABUNDLE', $inPost);
519  if (empty($mfaBundle)) {
520  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
521  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
522  }
523  $mfaBundle = openBundle($inPost['ORG'], $mfaBundle);
524  if (!checkBundle('MFC', $mfaBundle, $inPost)) {
525  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
526  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
527  }
528  if (!checkBundle('TIME', $mfaBundle, $inPost)) {
529  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL_TIMEOUT']);
530  throw new Exception("Authentication Failed Login Process Took Too Long", 15595); # bundle expired
531  }
532  # expect USERID, SAC
533  # if valid SAC check password (send password request?)
534  if (!HCU_array_key_exists('MFS_Auth', $inPost)) {
535  throw new Exception('Missing MFS Parameter', 2040);
536  }
537  if (!isValidAuthcode($userrec, $inPost['MFS_Auth'])) {
538  throw new Exception('Invalid Access Code', 15507);
539  }
540  $mfaBundle['mode'] = 'MFC';
541  $mfaBundle['haveSAC'] = 'SAC';
542  $mfaBundle['BUILDTIME'] = time();
543  $mfaBundle = createBundle($inPost['ORG'], $mfaBundle);
544  if (empty($mfaBundle)) {
545  throw new Exception("Authentication Failed Building Credential Package", 15597); # bundle failed
546  }
547  $HB_ENV['mfaBundle'] = $mfaBundle;
548  $reply_arr = PWD_prompt($dbh, $HB_ENV);
549  // this exits
550  send_response( $reply_arr, $SENDAS );
551  break;
552  case 'MFP':
553  $mfaBundle = HCU_array_key_value('MFABUNDLE', $inPost);
554  if (empty($mfaBundle)) {
555  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
556  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
557  }
558  $mfaBundle = openBundle($inPost['ORG'], $mfaBundle);
559  if (!checkBundle('MFP', $mfaBundle, $inPost)) {
560  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL']);
561  throw new Exception("Authentication Failed Invalid Credential Package", 15599); # bundle failed
562  }
563  if (!checkBundle('TIME', $mfaBundle, $inPost)) {
564  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_BNDL_TIMEOUT']);
565  throw new Exception("Authentication Failed Login Process Took Too Long", 15595); # bundle expired
566  }
567  # expect USERID, USERPASS
568  # if valid send MFAKEY, USERKEY, FULLFEED
569  # check USERPASS as password
570  if (strlen(trim($inPost['USERPASS'])) < 4 ||
571  !password_verify($inPost['USERPASS'], $HB_ENV['password'])) {
572  # password failed
573  LogFail($dbh, $HB_ENV, $inPost, $GLOBALS['MEM_LOGIN_FAILED_PWD']);
574  throw new Exception("Authentication Failed", 15505); # password
575  }
576 
577  break;
578  default:
579  throw new Exception("Unknown authentication mode", 2999);
580  break;
581  }
582 
583  // If we get here the user is authenticated either with the initial password being verified, or after
584  // completing the MFA process; any intermediate step in the MFA process would have sent a response
585  // back before this point.
586 
587  # logged in - now what?
588  LogPass($dbh,$HB_ENV);
589 
590  /*
591  * Desktop updates userrec and adds audit trail using this code:
592  * Consider similar?
593  *
594  *
595  * ALL of this next block will probably go away to mimic desktop
596  * Desktop no longer allows blank email, and the security reset
597  * should eventually force user to review confidence word and
598  * user contact phone numbers once the apps support that update set
599  *
600  * For now, clear the flags and assume having entered an accurate SAC
601  * constitutes resetting security.
602  *
603 
604  $updateEmail = (trim($userrec['email']) == '' && trim($mfaBundle['MFA_E']) > '');
605  $updateSecRes = (intval($HB_ENV['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')) > 0 && $HB_ENV['Ffreset'] == 2 );
606 
607  if ($updateSecRes || $updateEmail) {
608  $updTable = array('user' => array(
609  array(
610  "_action" => "update",
611  "user_id" => $HB_ENV['Uid']
612  )
613  )
614  );
615  if ($updateSecRes) {
616  // * CLEAR the MEM_FORCE_RESET bit in userflags
617  $valueUserFlag = (int) HCU_array_key_value('userflags', $userrec);
618  $valueUserFlag = ~(~$valueUserFlag | GetUserFlagsValue('MEM_FORCE_RESET'));
619  $updTable['user'][0]['userflags'] = $valueUserFlag;
620  $userrec['freset'] &= ~($GLOBALS['MEM_FORCE_RESET']); # shouldn't this be userrec['freset'] ?
621  $HB_ENV['forceupdate'] &= ~((int) 16);
622  }
623  if ($updateEmail) {
624  $HB_ENV['Ml'] = urlencode($mfaBundle['MFA_E']);
625  $userrec['email'] = $mfaBundle['MFA_E'];
626  $updTable['user'][0]['email'] = trim($mfaBundle['MFA_E']);
627  }
628  // * Update the records -- ONLY SAVE AUDIT RECORD IF UPDATING EMAIL
629  $updateResults = DataUserTableUpdate($dbh, $HB_ENV, $HB_ENV['MC'], $updTable, $userrec['user_id'], 'U_UPD', $HB_ENV['platform'], $HB_ENV['currentscript'], 'U', ($updateSecRes ? "Force Mbr Update / " : "") . ($updateEmail ? "Set Email" : ""), $userrec['user_name'], $mfaBundle['MFA_E'], $HB_ENV['remoteIp'], !$updateEmail);
630  }
631  $HB_ENV['Ffreset'] = $userrec['freset'];
632  */
633 
634  #
635  # password ok, if Force pwd or verify mail, send upd request or fall thru to send data
636 
637  $sendkeys = array('MFAKEY' => MakeMFAKey( $HB_ENV ),
638  'TIMEOUT' => $HB_ENV['AppTimeout'],
639  'menu' => 'REPLACE MENU HERE', // this is the menu from the appconfig and should be deprecated
640  );
641 
642  // if device asking to be remembered, return a device cookie (in parts)
643  $pass = array();
644  $postVar = array(
645  "REMEMBER_DEVICE" => array('filter' => FILTER_SANITIZE_STRING)
646  );
647 
648  HCU_ImportVars( $pass, "", $postVar );
649  if ( strtolower(HCU_array_key_value("REMEMBER_DEVICE", $pass)) == "yes" ) {
650  $mfaMode = (intval($userrec['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')));
651  $mfaDate = HCU_array_key_value("mfadate", $userrec);
652 
653  $persistsTime = $HB_ENV['SYSENV']['ticket']['persists'];
654 
655  $cookieParams = array ( "cu" => $HB_ENV['cu'],
656  "user_name" => $userrec['user_name'],
657  "saved_pass" => $userrec['passwd'],
658  "saved_email" => $userrec['email'],
659  "saved_confidence" => $userrec['confidence'],
660  "mfa_mode" => $mfaMode,
661  "mfa_date" => $mfaDate,
662  "persists_time" => $persistsTime
663  );
664 
665  $cookieInfo = CreateDeviceCookie( $cookieParams);
666 
667  $sendkeys["DEVICE_COOKIE"] = $cookieInfo["name"] . "|" . $cookieInfo["content"] . "|" . $cookieInfo["expire"];
668  }
669 
670  # password ok & 2-factor, send upd request if security reset
671  if ( $inPost["UPDAWARE"] == 1 ) {
672  if ($HB_ENV['forceupdate'] > 0 && $HB_ENV['offline'] == 'N') {
673  // return the information now
674 
675  // 3100 challenge response after login
676  $reply_arr = Return_ReqUpdate($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $HB_ENV, $sendkeys);
677  // this will exit the script
678  send_response( $reply_arr, $SENDAS );
679  }
680  } else if ( $inPost["UPDAWARE"] == 2 ) {
681  // check if need to return a cookie to help force user to ProfileRequire
682  $l_MbrSettings = Check_Member_Settings($dbh, $HB_ENV, $HB_ENV['MC']);
683 
684  if ( $l_MbrSettings['code'] != '000' ) {
685  $sendkeys["ENVINFO"] = BuildEnvironmentInfoCookie($HB_ENV, $inPost["UPDAWARE"]);
686  }
687  }
688 
689  // set an encryption key for app to use as needed (so not coded into app)
690  $appEncryptionKey = GetPayloadEncryptionKey(32);
691 
692 } // end if authmode != sso
693 
694 // Initialize the saved user cookie if it hasn't been set yet (now that HB_ENV is complete).
695 if ( empty( $gSavedUserkey ) ) {
696  $gSavedUserkey = BuildBaseSessionTicket( $HB_ENV );
697 }
698 
699 apache_note('user_name', "{$inPost['ORG']}:{$HB_ENV['Uid']}");
700 //$apptoken = MakeUserkey($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid']);
701 $apptoken = MakeSessionUserkey( $HB_ENV );
702 $dflt_date = date("Ymd", time() - (1 * 24 * 60 * 60)); # odyssey default start date is yesterday
703 $recent = date("Ymd", time() - (1 * 24 * 60 * 60));
704 
705 $dflt_end = date("Ymd", time() + (4 * 24 * 60 * 60)); # + 4 days
706 $DTSTART = (empty($inPost['DTSTART']) ? "$dflt_date" : $inPost['DTSTART']);
707 $DTEND = (empty($inPost['DTEND']) ? "$dflt_end" : $inPost['DTEND']);
708 $sqlend = sqlmdy($DTEND);
709 
710 $PASSWITH = HCU_array_key_value('PASSWITH', $inPost);
711 
712 /*
713  * for testing only - if passwith contains 'MYBAD', send an error
714  */
715 $pass = array();
716 parse_str($PASSWITH, $pass);
717 if (array_key_exists('MYBAD', $pass)) {
718  $mybad = HCU_array_key_value('MYBAD', $pass);
719  $message = "Okey Dokey";
720 
721  throw new Exception( $message , $mybad );
722 }
723 
724 switch (HCU_array_key_value('RQMODE', $inPost)) {
725  case "CookieTest":
726 // $myTicket = appTokenCookie($HB_ENV);
727  $myTicket = MakeSessionUserkey( $HB_ENV );
728 // parse_str($myTicket,$tick);
729 // $HB_ENV["SYSENV"]["logger"]->info("Result:\n$myTicket" . print_r($tick,true) );
730  $reply_arr = array('appToken' => urlencode($myTicket));
731  break;
732  case "PASSTHRU":
733 // $mycookie = appTokenCookie($HB_ENV);
734 
735  $now = time();
736  $expires = $now + $HB_ENV['SYSENV']['ticket']['expires'];
737 
738  $mycookie = "Ctime=$now&Cu={$HB_ENV['Cu']}&Cn={$HB_ENV['Cn']}&Uid={$HB_ENV['Uid']}&Ce=$expires&Ca=&Ffchg={$HB_ENV['Ffchg']}&Ffremain={$HB_ENV['Ffremain']}";
739  $mycookie .= "&Fset={$HB_ENV['Fset']}&Fset2=${HB_ENV['Fset2']}&Fset3={$HB_ENV['Fset3']}&Fhdays={$HB_ENV['histdays']}&Flite=0&Clw={$HB_ENV['livewait']}&Clu={$HB_ENV['lastupdate']}";
740  $mycookie .= "&Fplog={$HB_ENV['Fplog']}&Fflog={$HB_ENV['Fflog']}&Fmsg_tx={$HB_ENV['Fmsg_tx']}&Ml=" . urlencode(trim($HB_ENV['Ml'])) . "&Flang=$Flang&Ffreset={$HB_ENV['Ffreset']}";
741 
742  SetTicket($HB_ENV,"", $apptoken);
743 
744  $config = FeatureGateConfig::GetInstance($dbh);
745  $logPassthroughTicket = new CreditUnionGate(CreditUnionGate::LOG_PASSTHROUGH_TICKET_FEATURE, $config);
746  if ($logPassthroughTicket->WillPass($HB_ENV['Cu'])) {
747  $HB_ENV['SYSENV']['logger']->info("[AppFeedPassthrough] appToken=$apptoken;Cn={$HB_ENV['Cn']}");
748  }
749 
750  switch ($inPost['PASSTO']) {
751  # seems extra, but this way we can change stuff at homecu
752  # without having to recompile app.
753  case "CKIMAGE":
754  $PSCRIPT = $HB_ENV['homebankingpath'];
755  $PSCRIPT .= "/ImageSOLO.prg";
756  $PSCRIPT .= "?cu=${HB_ENV['Cu']}";
757  break;
758  case "EXTERNAL":
759  $PSCRIPT = urldecode($PASSWITH);
760  if (stripos($PSCRIPT, 'homecu') !== false) {
761  $delim = (strpos($PSCRIPT, '?') === false ? '?' : '&');
762  $PSCRIPT .= "{$delim}vanilla=1";
763  }
764  $PASSWITH = '';
765  break;
766  case "SSO":
767  $PSCRIPT = $HB_ENV['homebankingpath'];
768  $PSCRIPT .= "/hcuConnect.prg?cu=${HB_ENV['Cu']}&vanilla=1";
769  $pass = array();
770  parse_str($PASSWITH, $pass);
771  foreach ($pass as $key => $value) {
772  if ($key == 'MBRACCT') {
773  if (!empty($value)) {
774  $encryptedAccount = hcu_encrypturl($value, $HB_ENV['historyHash']);
775  $PSCRIPT .= "&account=$encryptedAccount";
776  }
777  } else {
778  $PSCRIPT .= "&$key=$value";
779  }
780  }
781  # empty out PASSWITH so it doesn't get added again below
782  $PASSWITH = '';
783  break;
784  default:
785  $PSCRIPT = $HB_ENV['homebankingpath'];
786  $PSCRIPT .= "/{$inPost['PASSTO']}";
787  $PSCRIPT .= "?cu=${HB_ENV['Cu']}";
788  }
789  if (!empty($PASSWITH))
790  $PSCRIPT .= "&" . urldecode($PASSWITH);
791 
792  header("Location: $PSCRIPT");
793  exit;
794 
795  break;
796  case "ESTMT_PDF":
797  # PERKEY is the period key for the desired statement
798  # this could just parse PASSWITH into the environment, but I want to only take the parts I expect -
799  $pass = array();
800  parse_str($PASSWITH, $pass);
801  $stId = $pass['PERKEY'];
802  $pAcct = $pass['MBRACCT'];
803  # if stId is blank or pAcct is blank throw error
804  if (empty($stId) || empty($pAcct)) {
805  throw new Exception("Missing eStatement parameters",4050); # ESTMT_PDF
806  }
807 
808  # Create_PDF_Statement will be responsible for the output
809  #ODYSSEY pass $pass['MBRACCT'] for this function
810  $pdfinfo = Create_PDF_Statement($stId, $HB_ENV, $MC,$pAcct);
811 
812  // if no errors, then exit because Create_PDF_Statement handled the download
813  if ( $pdfinfo["status"]["code"] == "0" ) {
814  exit;
815  } else {
816  // if we got here there is an error so get it from the returned info
817  $errorMessage = "";
818  for ( $i = 0; $i < count( $pdfInfo["errors"] ); $i++ ) {
819  if ( $errorMessage != "" ) {
820  $errorMessage .= ";";
821  }
822 
823  $errorMessage .= $pdfInfo["errors"];
824  }
825 
826  throw new Exception("PDF Download Error: $errorMessage", 4051); # ESTMT_PDF
827  }
828 
829  break;
830 
831  case "NOTICE":
832  #$PASSWITH="NOTICE_TYPE=${TYPE}&NOTICE_ID=${ID}&NOTICE_ACTION=${ACTION}&NOTICE_RESP=${ANSWERID}";
833  # TYPE indicates notice, survey, mktg message, promo etc
834  # ID is the notice id previously sent or 0 if this is a 'get'
835  # ACTION is P for post or V to view/get
836  # ANSWERID (optional) is the chosen response for survey/mktg message
837 # this could just parse PASSWITH into HB_ENV['HCUPOST'], but I want to only take the parts I expect -
838  $pass = array();
839  parse_str($PASSWITH, $pass);
840 
841  switch ($pass['NOTICE_ACTION']) {
842  case 'P':
843  # action P post so set up the HCUPOST array
844  if (!empty($pass['NOTICE_ID']))
845  $HB_ENV['HCUPOST']['notice_id'] = $pass['NOTICE_ID'];
846  if (!empty($pass['NOTICE_TYPE']))
847  $HB_ENV['HCUPOST']['notice_type'] = $pass['NOTICE_TYPE'];
848 
849  $resp_arr = ANS_list($pass);
850  $HB_ENV['HCUPOST']['notice_response'] = array();
851  foreach ($resp_arr as $key => $value) {
852  #print "key $key value $value <br>";
853  $HB_ENV['HCUPOST']['notice_response'][] = $value;
854  }
855 
856  $HB_ENV['HCUPOST']['notice_device'] = 'P';
857  $HB_ENV['HCUPOST']['notice_msg_show'] = 1;
858 
859  $response = Update_NoticeInfo($dbh, $HB_ENV, $MC);
860  $xmlResp = '';
861  $reply_arr = array();
862  # deal with response
863  if (count($response['status']['errors']) > 0) {
864  throw new Exception(implode(" ",$response['status']['errors']),15530); # Update_NoticeInfo
865  } else {
866  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
867  'DTSERVER' => date('YmdHis'),
868  'MEMBER' => $HB_ENV['Uid'],
869  'USERKEY' => $apptoken );
870 
871  $reply_arr['NOTICE'] = array();
872  if (HCU_array_item_count('notice_results', $response) > 0) {
873 
874  foreach ($response['notice_results'] as $noticekey => $details) {
875  $reply_arr['NOTICE']['NOTICE_TYPE'] = $details['notice_type'];
876  $reply_arr['NOTICE']['NOTICE_ID'] = $details['notice_id'];
877  $reply_arr['NOTICE']['NOTICE_POPUP'] = $details['notice_popup'];
878  if ($pass['NOTICE_TYPE'] == 'N') {
879  $reply_arr['NOTICE']['NOTICE_DONOTSHOWTEXT'] = htmlentities($details['notice_donotshowtext'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
880  $reply_arr['NOTICE']['NOTICE_LINKTARGET'] = $details['notice_linktarget'];
881  $reply_arr['NOTICE']['NOTICE_LINKDISPLAY'] = htmlentities($details['notice_linkdisplay'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
882  }
883  $reply_arr['NOTICE']['NOTICE_INTRO'] = htmlentities($details['notice_intro'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
884  $reply_arr['NOTICE']['NOTICE_TITLE'] = htmlentities($details['notice_title'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
885  $reply_arr['NOTICE']['NOTICE_TEXT'] = htmlentities(CleanWordQuotes($details['notice_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
886  $reply_arr['NOTICE']['NOTICE_ANSWERTYPE'] = $details['notice_answertype'];
887  $reply_arr['NOTICE']['NOTICE_ANSWERS'] = array();
888  if (count($details['notice_answers'])) {
889  foreach ($details['notice_answers'] as $anskey => $ansdetl) {
890  $reply_arr['NOTICE']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
891  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
892  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE),
893  'ANSWER_VOTES' => "{$ansdetl['answer_votes']}",
894  'ANSWER_PCT' => "{$ansdetl['answer_pct']}"));
895  }
896  }
897 
898  }
899  }
900  }
901 
902  break;
903 
904  case 'V':
905  case 'G': # gather multiple promos plus one survey/message
906  if ($pass['NOTICE_TYPE'] == 'N') {
907  # looks like the last two params are reversed?
908  $response = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', $pass['NOTICE_ID'], 0);
909  } else {
910  $response = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', $pass['NOTICE_TYPE']);
911  }
912 
913  if (count($response['status']['errors']) > 0) {
914  throw new Exception(implode(" ",$response['status']['errors']),15540); # Get_NoticeInfo
915  } else {
916  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
917  'MEMBER' => $HB_ENV['Uid'],
918  'USERKEY' => $apptoken );
919 
920  $reply_arr['NOTICE'] = array();
921  if (count($response['notice'])) {
922  foreach ($response['notice'] as $noticekey => $details) {
923  /*
924  * if no answer list but I have a donotshowtext, send that as an answer
925  */
926  if (!count($details['notice_answers']) && sizeof($details['notice_donotshowtext'])) {
927  $details['notice_answers'][] = array('answer_id' => $details['notice_id'], 'answer_text' => $details['notice_donotshowtext']);
928  $details['notice_answertype'] = 'M';
929  }
930  foreach ($details as $key => $value) {
931  $key = strtoupper($key);
932  switch ($key) {
933  case "NOTICE_ANSWERS":
934  $reply_arr['NOTICE']['NOTICE_ANSWERS'] = array();
935  foreach ($value as $anskey => $ansdetl) {
936  $reply_arr['NOTICE']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
937  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
938  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
939 
940  }
941  break;
942  case "NOTICE_TEXT":
943  if ($pass['NOTICE_TYPE'] != 'N') {
944  $reply_arr['NOTICE'][$key] = htmlentities(CleanWordQuotes($value), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
945  }
946  break;
947  /*
948  * ignore these
949  */
950  case "NOTICE_DONOTSHOWTEXT":
951  case "NOTICE_MSG_TX":
952  case "NOTICE_MSG_TX_SHOW":
953  case "NOTICE_MSG_TX_PERM":
954  case "NOTICE_SUPPRESSRESPONSE":
955  case "NOTICE_POSTTARGET":
956  break;
957  case "NOTICE_TYPE":
958  case "NOTICE_ID":
959  case "NOTICE_POPUP":
960  case "NOTICE_ANSWERTYPE":
961  case "NOTICE_LINKTARGET":
962  case "NOTICE_LINKDISPLAY":
963  case "NOTICE_INTRO":
964  case "NOTICE_TITLE":
965  default:
966 
967  $reply_arr['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
968  }
969  }
970  }
971  }
972  }
973 
974  break;
975 
976  default:
977  # unknown notice_action -- ignore?
978  throw new Exception("Unknown Notice Mode",15550);
979  }
980 
981  break;
982 
983  case "TXLIST":
984  # before we get started, check permissions
985  $accessRights = Perm_AccessRights( $dbh, $HB_ENV, array( "feature" => FEATURE_TRANSFERS ) );
986  if ( ! HCU_array_key_value('create', $accessRights)) {
987  throw new Exception( $HB_ENV['MC']->msg('Rights not set', HCU_DISPLAY_AS_HTML), 915 );
988  }
989 
990  $HB_ENV['allowReadonly'] = false;
991  if (!hcu_checkOffline($dbh, $HB_ENV)) {
992  throw new Exception($HB_ENV['offlineblurb'], 15520); # TXLIST cu not online
993  }
994 
995  # AppFeed will accept CFGFLAG from app, set it in HB_ENV so it passes to TX_list / TX_post
996  # this was to maintain Fmsg_tx session flags w/o html session
997  # but didn't want to limit to ONLY Fmsg_tx - might need others in future
998  # coding ready for test in OFXRequest but not ready in server functions
999  #if cfgflag came in from app, pass it along on TX_list / TX_post call
1000  # look for updated setting (of cfgflag? or of Fmsg_tx?) on return
1001  # pass updated setting to app so they can return it again?
1002  // $HB_ENV['Fmsg_tx'] = ($HB_ENV['Fmsg_tx'] | $HB_ENV['cfgflag']);
1003  $txreturn = TX_list($dbh, $HB_ENV);
1004 
1005  if (HCU_array_key_exists("Fmsg_tx", $txreturn['status'])) {
1006  // ** ONLY update Fmsg_tx if it was updated by TX_list
1007  $HB_ENV['Fmsg_tx'] = $txreturn['status']['Fmsg_tx'];
1008  }
1009  $gSavedUserkey = BuildBaseSessionTicket($HB_ENV);
1010  $apptoken = MakeSessionUserkey($HB_ENV);
1011 
1012  if ( HCU_array_item_count("errors", $txreturn['status']) > 0 ) {
1013  throw new Exception(implode(' ', $txreturn['status']['errors']), 15560); # TX_list returns System Unavailable
1014  } else {
1015  $fc = 0;
1016  foreach ($txreturn['acctlist'] as $tx) {
1017  if ($tx['from'] == 'Y')
1018  $fc++;
1019  }
1020 
1021  # FIX FOR PROD - Error checking, anyone?
1022  # send appropriate status if we can't get a list
1023 
1024  if ($fc == 0) {
1025  throw new Exception("No Accounts Available", 15570); # TX_list no valid accounts
1026  } else {
1027  $reply_arr = array();
1028  $reply_arr = array('STATUS' => array('CODE' => 0, 'SEVERITY' => 'INFO'),
1029  'MEMBER' => $HB_ENV['Uid'],
1030  'USERKEY' => $apptoken,
1031  'TRMEMOMAX' => $HB_ENV['trmemomaxlen']);
1032 
1033  foreach ($txreturn['acctlist'] as $acctkey => $details) {
1034  $acct_arr = array();
1035  $acct_arr['ACCTID'] = $acctkey;
1036  foreach ($details as $key => $value) {
1037  $key = strtoupper($key);
1038  $val = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1039  $acct_arr[$key] = $val;
1040  }
1041 
1042  $reply_arr['TXLIST'][]['ACCT'] = $acct_arr;
1043  }
1044 
1045  # now get the transfer notice, if any
1046  $txnotice = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'transferNotice', 1);
1047 
1048  if (count($txnotice['notice'])) {
1049  foreach ($txnotice['notice'] as $noticekey => $details) {
1050  /*
1051  * if no answer list but I have a donotshowtext, send that as an answer
1052  */
1053  if (!count($details['notice_answers']) && sizeof($details['notice_donotshowtext'])) {
1054  $details['notice_answers'][] = array('answer_id' => $details['notice_id'], 'answer_text' => $details['notice_donotshowtext']);
1055  $details['notice_answertype'] = 'M';
1056  }
1057 
1058  foreach ($details as $key => $value) {
1059  $key = strtoupper($key);
1060  switch ($key) {
1061  case "NOTICE_ANSWERS":
1062  $reply_arr['TXLIST']['NOTICE']['NOTICE_ANSWERS'] = array();
1063  foreach ($value as $anskey => $ansdetl) {
1064  $reply_arr['TXLIST']['NOTICE']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
1065  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
1066  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
1067  }
1068  break;
1069  /*
1070  * ignore these
1071  */
1072  case "NOTICE_DONOTSHOWTEXT":
1073  case "NOTICE_MSG_TX":
1074  case "NOTICE_MSG_TX_SHOW":
1075  case "NOTICE_MSG_TX_PERM":
1076  case "NOTICE_SUPPRESSRESPONSE":
1077  case "NOTICE_POSTTARGET":
1078  case "NOTICE_TEXT":
1079  break;
1080  case "NOTICE_TYPE":
1081  case "NOTICE_ID":
1082  case "NOTICE_POPUP":
1083  case "NOTICE_ANSWERTYPE":
1084  case "NOTICE_LINKTARGET":
1085  case "NOTICE_LINKDISPLAY":
1086  case "NOTICE_INTRO":
1087  case "NOTICE_TITLE":
1088  default:
1089 
1090  $reply_arr['TXLIST']['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1091  } // end switch
1092  } // end foreach
1093  } // end foreach
1094  } // end if transfer notice
1095 
1096  $allowScheduled = (($HB_ENV['flagset2'] & $GLOBALS['CU2_PROCRECUR']) && PermCheckFeatureScreen($dbh, $HB_ENV, $MC, FEATURE_SCHEDULED_TRANSFERS, '', false));
1097 
1098  if ( $allowScheduled ) {
1099  $reply_arr["SCHEDULED"]["SCHEDALLOW"] = "yes";
1100 
1101  // get the transfer frequency "continue until" lists
1102  $transferFrequencyList = TxIntervalList($HB_ENV["MC"]);
1103  $transferContinueList = TxContinueList($HB_ENV["MC"]);
1104 
1105  // pass the lists to the apps
1106  if ( count( $transferFrequencyList ) > 0 ) {
1107  $freqList = array();
1108  for ( $i = 0; $i < count( $transferFrequencyList ); $i++ ) {
1109  $freqList[]["FREQUENCY"] = array( "NAME" => $transferFrequencyList[$i]["value"],
1110  "VALUE" => $transferFrequencyList[$i]["text"] );
1111  }
1112 
1113  $reply_arr["SCHEDULED"]["FREQUENCIES"] = $freqList;
1114  }
1115 
1116  if ( count( $transferContinueList ) > 0 ) {
1117  $contList = array();
1118  for ( $i = 0; $i < count( $transferContinueList ); $i++ ) {
1119  $contList[]["CONTINUE"] = array( "NAME" => $transferContinueList[$i]["value"],
1120  "VALUE" => $transferContinueList[$i]["text"] );
1121  }
1122  $reply_arr["SCHEDULED"]["CONTINUES"] = $contList;
1123  }
1124 
1125  // get the scheduled transfer terms, if any
1126  $aryScheduledTerms = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'rptTransferTerms');
1127 
1128  $reply_arr['SCHEDULED']['TERMS'] = array();
1129  if (count($aryScheduledTerms['notice'])) {
1130  foreach ($aryScheduledTerms['notice'] as $noticekey => $details) {
1131  foreach ($details as $key => $value) {
1132  $key = strtoupper($key);
1133  switch ($key) {
1134  case "NOTICE_ANSWERS":
1135 
1136  $reply_arr['SCHEDULED']['TERMS']['NOTICE_ANSWERS'] = array();
1137  foreach ($value as $anskey => $ansdetl) {
1138  $reply_arr['SCHEDULED']['TERMS']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
1139  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
1140  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
1141  }
1142  break;
1143  /*
1144  * ignore these
1145  */
1146  case "NOTICE_INTRO":
1147  case "NOTICE_TITLE":
1148  case "NOTICE_DONOTSHOWTEXT":
1149  case "NOTICE_MSG_TX":
1150  case "NOTICE_MSG_TX_SHOW":
1151  case "NOTICE_MSG_TX_PERM":
1152  case "NOTICE_SUPPRESSRESPONSE":
1153  case "NOTICE_POSTTARGET":
1154  case "NOTICE_TEXT":
1155  break;
1156  case "NOTICE_TYPE":
1157  case "NOTICE_ID":
1158  case "NOTICE_POPUP":
1159  case "NOTICE_ANSWERTYPE":
1160  case "NOTICE_LINKTARGET":
1161  case "NOTICE_LINKDISPLAY":
1162  default:
1163 
1164  $reply_arr['SCHEDULED']['TERMS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1165  } // end switch
1166  } // end foreach
1167  } // end foreach
1168  } // end if
1169  } // end if allow scheduled
1170  } // end else
1171  } // end else
1172 
1173  break;
1174 
1175  case "TXPOST":
1176  # before we get started, check permissions
1177  # but OdyTxPost function checks near the top.... so skip this one?
1178 // $accessRights = Perm_AccessRights( $dbh, $HB_ENV, array( "feature" => FEATURE_TRANSFER ) );
1179 // if ( ! HCU_array_key_value('create', $accessRights)) {
1180 // throw new Exception( $HB_ENV['MC']->msg('Rights not set', HCU_DISPLAY_AS_HTML), 915 );
1181 // }
1182 
1183  $HB_ENV['allowReadonly'] = false;
1184  if (!hcu_checkOffline($dbh, $HB_ENV)) {
1185  throw new Exception($HB_ENV['offlineblurb'],15520); # TXPOST cu not online
1186  exit;
1187  }
1188  # unencode the email for the txpost call
1189 // $HB_ENV['Ml'] = urldecode($HB_ENV['Ml']);
1190 # OFXRequest will accept CFGFLAG from app, set it in HB_ENV so it passes to TX_list / TX_post
1191 # this was to maintain Fmsg_tx session flags w/o html session
1192 # but didn't want to limit to ONLY Fmsg_tx - might need others in future
1193 # coding ready for test in OFXRequest but not ready in server functions
1194 #if cfgflag came in from app, pass it along on TX_list / TX_post call
1195 # look for updated setting (of cfgflag? or of Fmsg_tx?) on return
1196 # pass updated setting to app so they can return it again?
1197 // $HB_ENV['Fmsg_tx'] = ($HB_ENV['Fmsg_tx'] | $HB_ENV['cfgflag']);
1198 
1199 // $txreturn = TX_post($MC, $dbh, $HB_ENV, $inPost['FACCTID'], $inPost['TACCTID'], $inPost['AMOUNT'], $inPost['TRMEMO']);
1200 
1201  // gather the necessary input variables
1202  $pass = array();
1203  $postVar = array(
1204  "FACCTID" => array('filter' => FILTER_SANITIZE_STRING),
1205  "TACCTID" => array('filter' => FILTER_SANITIZE_STRING),
1206  "TXFREQ" => array('filter' => FILTER_SANITIZE_STRING),
1207  "TXSTART" => array('filter' => FILTER_SANITIZE_STRING),
1208  "TXCONTINUE" => array('filter' => FILTER_SANITIZE_STRING),
1209  "TXEND" => array('filter' => FILTER_SANITIZE_STRING),
1210  "AMOUNT" => array('filter' => FILTER_SANITIZE_STRING),
1211  "TRMEMO" => array('filter' => FILTER_SANITIZE_STRING)
1212  );
1213 
1214  HCU_ImportVars( $pass, "", $postVar );
1215 
1216  // test the frequency value for existence because that is a new value
1217  if ( HCU_array_key_exists( "TXFREQ", $pass ) ) {
1218  $txReturn = TxPostAdvanced( $HB_ENV, $dbh, $pass, $HB_ENV["MC"] );
1219 
1220  if ( HCU_array_item_count('errors',$txReturn['status']) > 0 ) {
1221  throw new Exception(implode(' ',$txReturn['status']['errors']),15580); # TX_post returns data validation errors
1222  }
1223 
1224  // here we are successful, so return information as given
1225  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO'),
1226  'DTSERVER' => date('YmdHis'),
1227  'MEMBER' => $HB_ENV['Uid'],
1228  'USERKEY' => $apptoken );
1229 
1230  // return all the txn info we got
1231  foreach ($txReturn['txn'] as $key => $value) {
1232  $key = strtoupper($key);
1233  $val = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1234  $reply_arr['TXPOST'][$key] = $val;
1235  }
1236 
1237  if ( HCU_array_key_exists( "data_confirm", $txReturn["txn"] ) ) {
1238  // some apps still expecting this
1239  $reply_arr['TXPOST']["CONFIRMID"] = $txReturn["txn"]["data_confirm"];
1240  }
1241  } else {
1242  // call the original way and return info the original way
1243  $txreturn = OdyTxPost( $dbh, $HB_ENV, $pass, $HB_ENV['MC'] );
1244 
1245  if ( HCU_array_item_count('errors',$txreturn['status']) > 0 ) {
1246  throw new Exception(implode(' ',$txreturn['status']['errors']),15580); # TX_post returns data validation errors
1247  }
1248 
1249  // here we are successful, so return information as given
1250  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO'),
1251  'DTSERVER' => date('YmdHis'),
1252  'MEMBER' => $HB_ENV['Uid'],
1253  'USERKEY' => $apptoken );
1254 
1255  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1256  'DTSERVER' => date('YmdHis'),
1257  'MEMBER' => $HB_ENV['Uid'],
1258  'USERKEY' => $apptoken );
1259 
1260  foreach ($txreturn['txn'] as $key => $value) {
1261  $key = strtoupper($key);
1262  $val = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1263  $reply_arr['TXPOST'][$key] = $val;
1264  }
1265 
1266  if ( HCU_array_key_exists( "data_confirm", $txreturn["txn"] ) ) {
1267  // some apps still expecting this
1268  $reply_arr['TXPOST']["CONFIRMID"] = $txreturn["txn"]["data_confirm"];
1269  }
1270  }
1271 
1272  break;
1273 
1274  case "MEMBERSETTINGS":
1275  #$PASSWITH contains
1276  // GETALLOWED=1 for Show Allowed Settings
1277  // OR
1278  // GETSETTINGS=2 for Email
1279  // GETSETTINGS=4 for Challenge
1280  // GETSETTINGS=8 for UserAlias
1281  // GETSETTINGS=16 for Contact Phones (coming soon...)
1282  // GETSETTINGS values can be combined - 10 would get both email & alias
1283  #
1284  $pass = array();
1285  parse_str($PASSWITH, $pass);
1286 
1287  if (HCU_array_key_value('GETALLOWED',$pass) == '1') {
1288  $reply_arr = Return_AllowedUpdate($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $HB_ENV);
1289  } else {
1290  $HB_ENV['requpdate'] = ($pass['GETSETTINGS'] & $HB_ENV['allowupdate']); #limit acceptable values based on cu config
1291  if ($HB_ENV['requpdate'] == 0) {
1292  throw new Exception('Invalid Settings Request',3125); # unrecognized GETSETTINGS request or not configured
1293  } else {
1294  $reply_arr = Return_ReqUpdate($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $HB_ENV, array());
1295  }
1296  }
1297  break;
1298  case "CHECKALIAS":
1299  $pass = array();
1300  parse_str($PASSWITH, $pass);
1301  $upd_fields['settings_alias']['username'] = $pass['UALIAS'];
1302  $upd_fields['settings_alias']['username_confirm'] = $pass['UALIAS'];
1303  $upd_fields['settings_alias']['username_required'] = ($HB_ENV['alias'] == 'REQUIRE' ? 'Y' : 'N');
1304  $aryUpdate = Validate_Settings($dbh, $HB_ENV, $upd_fields, $MC);
1305 
1306  if ($aryUpdate['status']['code'] == '000') {
1307  $reply_arr = Return_ResponseOK($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], 0, "OK Selected Alias is valid and available"); # Selected Alias is valid and available
1308  } else {
1309  throw new Exception(implode(' ',$aryUpdate['status']['errors']), 3162); # Selected Alias is not valid or usable
1310  }
1311  break;
1312 
1313  case "UPDCRED":
1314  #$PASSWITH contains name=value pairs for each credential to be updated
1315 # this could just parse PASSWITH into HB_ENV['HCUPOST'], but I want to only take the parts I expect -
1316  $pass = array();
1317  parse_str($PASSWITH, $pass);
1318 //$HB_ENV["SYSENV"]["logger"]->info(__LINE__ . " UPDCRED with " . print_r($pass, true));
1319 
1320  if (HCU_array_key_value('ASKLATER',$pass) == 1) {
1321  if ($HB_ENV['forceupdate'] > 0 && $HB_ENV['Ffremain'] == 0) {
1322  throw new Exception('Requested Updates cannot be deferred',3150); # got 'ask later' response w/0 grace logins remaining
1323  } else {
1324  $sendkeys = array('USERKEY' => $apptoken,
1325  'MFAKEY' => MakeMFAKey( $HB_ENV ),
1326  'TIMEOUT' => $HB_ENV['AppTimeout'],
1327  'menu' => 'REPLACE MENU HERE'); # after update settings
1328  $reply_arr = Return_ResponseOK($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $sendkeys, "OK Updates Deferred");
1329  }
1330  } else {
1331  # process updates
1332  # start out thinking we won't need a transaction
1333  $bolSQLTransaction = false;
1334  $upd_fields=array();
1335 
1336  if (($HB_ENV['allowupdate'] & 4) == 4) {
1337  # if challenge stuff allowed and provided, set fields to update,
1338  // CQID_*, CQANS_*, CONFWORD, CURPWD
1339  if (!empty($pass['CONFWORD'])) {
1340  $upd_fields['settings_confidence']['confword'] = $pass['CONFWORD'];
1341  }
1342  // ** Challenge Questions / Responses
1343  // ** loop through the question, just assume they start at 1 and go to the number defined in HB_ENV['cu_chgqst_count']
1344  $quest_idx = 1;
1345  while (array_key_exists("CQID_{$quest_idx}", $pass)) {
1346  $upd_fields['settings_questions'][] = Array('cqid' => $pass["CQID_{$quest_idx}"], 'display' => $pass["CQANS_{$quest_idx}"]);
1347  $quest_idx++;
1348  }
1349  if (HCU_array_key_exists('userflags',$HB_ENV) && HCU_array_key_exists('settings_questions',$upd_fields) && ($HB_ENV['forceupdate'] & 4) == 4 ) {
1350  // * have some userflags and got some questions, reset the MEM_FORCE_RESET flag
1351 
1352  $tmpVal = (int) HCU_array_key_value('userflags', $HB_ENV);
1353  // ** NEGATE the MEM_FORCE_RESET value
1354  $tmpVal = ~(~$tmpVal | GetUserFlagsValue('MEM_FORCE_RESET'));
1355 
1356  $upd_fields['settings_confidence']['userflags'] = $tmpVal;
1357 // $HB_ENV["SYSENV"]["logger"]->info(__LINE__ . " update with " . print_r($upd_fields,true));
1358  }
1359 
1360  }
1361  // if passwd stuff, set fields to update,
1362  // NEWPWD, CURPWD
1363  if (trim(HCU_array_key_value('NEWPWD',$pass)) > '') {
1364  $upd_fields['settings_password']['newpasswd'] = $pass['NEWPWD'];
1365  $upd_fields['settings_password']['confpasswd'] = $pass['NEWPWD'];
1366  }
1367  // if no error, change pwchange date, clear Ffchange
1368  //
1369  // if email stuff, set fields to update,
1370  // EMAIL, OPTIN
1371  if (!empty(HCU_array_key_value('EMAIL',$pass))) {
1372  $upd_fields['settings_email']['email'] = $pass['EMAIL'];
1373  $upd_fields['settings_email']['egenl'] = $pass['OPTIN'];
1374  $upd_fields['settings_email']['verify'] = ($HB_ENV['Fverifyml'] == 512 ? 'Y' : 'N');
1375  $upd_fields['settings_email']['valid'] = 'Y';
1376  }
1377  //
1378  // if alias stuff allowed/required and provided, set fields to update
1379  // UALIAS, CURPWD
1380  if (($HB_ENV['allowupdate'] & 8) == 8 && (HCU_array_key_value('UALIAS',$pass)) != '') {
1381  $upd_fields['settings_alias']['username'] = $pass['UALIAS'];
1382  $upd_fields['settings_alias']['username_confirm'] = $pass['UALIAS'];
1383  $upd_fields['settings_alias']['username_required'] = ($HB_ENV['alias'] == 'REQUIRE' ? 'Y' : 'N');
1384  $bolSQLTransaction = true;
1385  }
1386  if (($HB_ENV['allowupdate'] & 16) == 16) {
1387  # if contact phones allowed and provided, set fields to update,
1388  // PHONE_*, CONFWORD, CURPWD
1389  if (!empty($pass['CONFWORD'])) {
1390  $upd_fields['settings_confidence']['confword'] = $pass['CONFWORD'];
1391  }
1392  // ** Phone numbers
1393  // ** posted in like this: PHONE=1112223333|1112223333|
1394  // update function expects the punctuation
1395  if (HCU_array_key_exists('PHONE',$pass)) {
1396  $phones = explode('|',HCU_array_key_value('PHONE',$pass));
1397  # empty array will save empty list (delete pre-existing)
1398  $upd_fields['settings_phones']['mobile']=array();
1399 
1400  # insert punctuation
1401  $phidx=0;
1402  foreach($phones as $phnum) {
1403  if (!empty($phnum) && $phidx <= MAX_PHONES) {
1404  $upd_fields['settings_phones']['mobile'][] = format_us_number($phnum);
1405  $phidx++;
1406  }
1407  }
1408  if (HCU_array_key_exists('userflags',$HB_ENV) && ($HB_ENV['forceupdate'] & 16) == 16 ) {
1409  // * have some userflags and got some phones, reset the MEM_FORCE_RESET flag
1410  $tmpVal = (int) HCU_array_key_value('userflags', $HB_ENV);
1411  // ** NEGATE the MEM_FORCE_RESET value
1412  $tmpVal = ~(~$tmpVal | GetUserFlagsValue('MEM_FORCE_RESET'));
1413  $upd_fields['settings_confidence']['userflags'] = $tmpVal;
1414  }
1415  }
1416  }
1417  //
1418 # start
1419  if (trim(HCU_array_key_value('NEWPWD',$pass)) > '') {
1420  $aryUpdate = Validate_PwdRules($dbh, $HB_ENV, $upd_fields, $MC);
1421 
1422  if ($aryUpdate['status']['code'] != '000') {
1423  // return validation errors
1424  throw new Exception(implode(' ',$aryUpdate['status']['errors']),3160); # Validate_Settings
1425  }
1426  }
1427  if (!count($upd_fields)) {
1428  throw new Exception('Nothing found to update',3165); # UPDCRED found nothing to update
1429  }
1430  // ** $upd_fields contains the list of fields to update --
1431  $aryUpdate = Validate_Settings($dbh, $HB_ENV, $upd_fields, $MC);
1432 
1433  if ($aryUpdate['status']['code'] != '000') {
1434  // return validation errors
1435  throw new Exception(implode(' ',$aryUpdate['status']['errors']),3160); # Validate_Settings
1436  #print_r($HB_ENV); print_r($upd_fields); exit;
1437  } else {
1438  if (isset($upd_fields['settings_phones']['mobile']) && ($HB_ENV['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')) ) {
1439  // since the security phone number are part of a different table
1440  // we must update them separately from the Update_User_Settings function.
1441  // only update if we have some phone numbers and the mfa secure access code flag is set
1442  $aryUpdate = Update_User_Security($dbh, $HB_ENV, $HB_ENV['MC'], $upd_fields);
1443  if ($aryUpdate['status']['code'] != '000') {
1444  throw new Exception(implode(' ',$aryUpdate['status']['errors']),3172); # Update_User_Security
1445  }
1446  }
1447  // ** UPDATE DATA
1448  $aryUpdate = Update_User_Settings($dbh, $HB_ENV, $MC, $upd_fields, $bolSQLTransaction);
1449 
1450  if ($aryUpdate['status']['code'] != '000') {
1451  // return update errors
1452  throw new Exception(implode(' ',$aryUpdate['status']['errors']),3170); # Update_Settings
1453  } else {
1454  // * SUCCESSFUL
1455  // update HB_ENV and the userrec before building keys
1456  $MEMBER = (HCU_array_key_value('UALIAS',$pass) ? HCU_array_key_value('UALIAS',$pass) : $MEMBER);
1457  Load_HB_ENV($dbh, $CU, $MEMBER, $HB_ENV, $CFGFLAG=0);
1458  $userrec = GetUserbyName($dbh, $inPost['ORG'], $MEMBER);
1459 
1460  $HB_ENV['Ce'] = time() + $HB_ENV['SYSENV']['ticket']['expires'];
1461  $HB_ENV['Clu'] = (empty($userrec['lastupdate']) ? $MC->msg("Unknown") : urlencode(trim($userrec['lastupdate'])));
1462  $HB_ENV['Fhdays'] = $userrec['fhdays'];
1463 
1464  // re-build the cookie with the values that changed
1465  $gSavedUserkey = BuildBaseSessionTicket( $HB_ENV );
1466 
1467  // return 'OK' status
1468  $apptoken = MakeSessionUserkey( $HB_ENV );
1469  $sendkeys = array('USERKEY' => $apptoken,
1470  'MFAKEY' => MakeMFAKey( $HB_ENV ),
1471  'TIMEOUT' => $HB_ENV['AppTimeout'],
1472  'menu' => 'REPLACE MENU HERE'); # after update settings
1473  $reply_arr = Return_ResponseOK($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $sendkeys, "OK Updates Successful");
1474  }
1475  }
1476 
1477 # end
1478  }
1479  break;
1480  case "MOBLPAY":
1481  # before we get started, check permissions
1482  $accessRights = Perm_AccessRights( $dbh, $HB_ENV, array( "feature" => FEATURE_MOBILE_BILLPAY ) );
1483  if ( ! HCU_array_key_value('access', $accessRights)) {
1484  throw new Exception( $HB_ENV['MC']->msg('Rights not set', HCU_DISPLAY_AS_HTML), 915 );
1485  }
1486 
1487  $parms = array();
1488  $parms['Cu'] = $HB_ENV['Cu'];
1489  $parms['Cn'] = $HB_ENV['Cn'];
1490  $parms['Uid'] = $HB_ENV['Uid'];
1491 
1492  $pass = array();
1493  parse_str($PASSWITH, $pass);
1494  if (!isset($pass['MP_VENDOR']) || !isset($pass['MBRACCT'])) {
1495  throw new Exception("Missing Mobile Pay parameters",4001);
1496  }
1497  switch ($pass['MP_VENDOR']) {
1498  case 'IPAYMBL':
1499  case 'IPAYAPP':
1500  case 'IPAYTEST':
1501  case 'IPAYBPS':
1502  if (hcu_checkService($dbh, 'IPAY') !== true) {
1503  $omsg = hcu_checkServiceMsg($dbh, "IPAY");
1504  throw new Exception("$omsg",999); # MOBLPAY AUTH
1505  }
1506  if ($pass['MP_VENDOR'] == 'IPAYBPS') {
1507  require_once(dirname(__FILE__) . '/../library/bpCommon.i'); # HCU functions common to all BILL Pay vendors
1508  require_once(dirname(__FILE__) . '/../library/IPAYBPS.i');
1509  } else {
1510  require_once(dirname(__FILE__) . '/../library/IPAYMBL.i');
1511  }
1512  break;
1513  case 'CHKFREE':
1514  if (hcu_checkService($dbh, 'CHKFREE') !== true) {
1515  $omsg = hcu_checkServiceMsg($dbh, "CHKFREE");
1516  throw new Exception("$omsg",999); # MOBLPAY AUTH
1517  }
1518  require_once(dirname(__FILE__) . '/../library/CHKFREE.i');
1519  break;
1520  case 'PSCUPAY_API':
1521  if (hcu_checkService($dbh, 'PSCU') !== true) {
1522  $omsg = hcu_checkServiceMsg($dbh, "PSCU");
1523  throw new Exception("$omsg",999); # MOBLPAY AUTH
1524  }
1525  require_once(dirname(__FILE__) . '/../library/PSCUPAY_API.i');
1526  break;
1527  case 'MBLPAY_TEST':
1528  # HomeCU test stub
1529  require_once(dirname(__FILE__) . '/../library/MBLPAY_TEST.i');
1530  break;
1531  default:
1532  throw new Exception("Invalid Mobile Pay vendor",4001);
1533  break;
1534  }
1535  $parray['Cu'] = $Cu;
1536  $parray['trustedid'] = $pass['MP_VENDOR'];
1537  $trusted = cutd_read($dbh, $parray);
1538  if ($trusted['status']['Response'] == 'false') {
1539 
1540  throw new Exception("Bill Pay Service not configured",2076); # no trusted vendor rec
1541  }
1542  $parms = $trusted['data']["$Cu|{$pass['MP_VENDOR']}"];
1543 
1544  # set up logging here
1545  $loggingFlag = trim($parms["hcuLogging"]);
1546  if (strlen($loggingFlag) > 0) {
1547 
1548  $enable = $loggingFlag == -1;
1549 
1550  if (!$enable) {
1551  $loggingFlag = str_replace(" ", "", $loggingFlag);
1552  $testArray = explode(",", $loggingFlag);
1553  $enable = in_array($HB_ENV["Cn"], $testArray);
1554  }
1555 
1556  if ($enable) {
1557  // these are used inside the plugin to test if logging and info to log.
1558  $parms["logging"] = "enabled";
1559  $parms["environment"] = array("Cu" => $HB_ENV["Cu"], // credit union
1560  "memberId" => $HB_ENV["Cn"], // member id
1561  "SSOVendor" => $pass['MP_VENDOR'],
1562  "userIP" => $_SERVER['REMOTE_ADDR'], // user's ip address
1563  "dbConn" => $dbh); // database connection
1564  }
1565  }
1566 
1567  $billpay = Get_Billpayid($dbh, $HB_ENV, $pass['MP_VENDOR'], $pass['MBRACCT']);
1568  $parms['BillpayId'] = trim($billpay['billpayid']);
1569  $parms['passwith'] = $pass;
1570  switch ($pass['MP_ACTION']) {
1571  case "MP_AUTH":
1572  $mblpay = bpAuth($parms);
1573  # deal with response
1574 #
1575  if ($mblpay['status']['code'] == '000' && strlen($mblpay["data"]['Token']) > 0) {
1576  # good token, return the response
1577  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1578  'DTSERVER' => date('YmdHis'),
1579  'MEMBER' => $HB_ENV['Uid'],
1580  'MBRACCT' => $pass['MBRACCT'],
1581  'USERKEY' => $apptoken );
1582 
1583  if (is_array($mblpay['data'])) {
1584  $reply_arr['MP_AUTH'] = array();
1585  foreach ($mblpay['data'] as $key => $value) {
1586 // $key = strtoupper($key);
1587 // $xmlResp .= "<$key>$value</$key>\n";
1588  $reply_arr['MP_AUTH'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1589  }
1590  }
1591 
1592  } else {
1593  // ** If Error messages were included in the array that is returned, then show those errors, if NOT default to feature not set message
1594  // ** Show custom errors here - no MobilePay
1595  $noticesAry = Get_NoticeInfo($dbh, $HB_ENV, $MC, "M", "mblNoMobilePay", true);
1596 
1597  if ($noticesAry["status"]["code"] == "000" && $noticesAry["notice"][0]["notice_id"]) {
1598  $noticeString = $noticesAry["notice"][0]["notice_text"];
1599  } else {
1600  $noticeString = $mblpay['status']['message'];
1601  }
1602 
1603  throw new Exception($noticeString,$mblpay['status']['code']); # MOBLPAY AUTH get token failed
1604  }
1605  break;
1606 
1607  case "MP_TERMS":
1608  $mblpay = bpGetTerms($parms);
1609  # deal with response
1610  if ($mblpay['status']['response'] == 'false') {
1611  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY AUTH
1612  }
1613 
1614  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1615  'DTSERVER' => date('YmdHis'),
1616  'MEMBER' => $HB_ENV['Uid'],
1617  'USERKEY' => $apptoken,
1618  'TOKEN' => $pass['Token'] );
1619 
1620  if (is_array($mblpay['data'])) {
1621  $reply_arr['MP_TERMS'] = array();
1622  foreach ($mblpay['data'] as $key => $value) {
1623 // $key = strtoupper($key);
1624 // $xmlResp .= "<$key>$value</$key>\n";
1625  $reply_arr['MP_TERMS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1626  }
1627  }
1628 
1629  break;
1630  case "MP_ACCEPT":
1631  $mblpay = bpAcceptTerms($parms);
1632  # deal with response
1633  if ($mblpay['status']['response'] == 'false') {
1634  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY AUTH
1635  }
1636 
1637 
1638  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1639  'DTSERVER' => date('YmdHis'),
1640  'MEMBER' => $HB_ENV['Uid'],
1641  'USERKEY' => $apptoken,
1642  'TOKEN' => $pass['Token'] );
1643 
1644  if (is_array($mblpay['data'])) {
1645  $reply_arr['MP_ACCEPT'] = array();
1646  foreach ($mblpay['data'] as $key => $value) {
1647 // $key = strtoupper($key);
1648 // $xmlResp .= "<$key>$value</$key>\n";
1649  $reply_arr['MP_ACCEPT'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1650  }
1651  }
1652 
1653  break;
1654  case "MP_ACCTLIST":
1655  # set Account type for from
1656  # AcctType = Payment | Transfer
1657  # Detailed = 'True' | 'False' (may be missing, false)
1658  $mblfrom = bpSourceAccts($parms);
1659  # determine payment or transfer, set Account type accordingly
1660  $mblpay = bpDestAccts($parms);
1661 
1662  # deal with response
1663  if ($mblfrom['status']['response'] == 'false') {
1664  throw new Exception($mblfrom['status']['message'], $mblfrom['status']['code']); # MOBLPAY SourceAccts
1665  }
1666  if ($mblpay['status']['response'] == 'false') {
1667  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY DestAccts
1668  }
1669  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1670  'DTSERVER' => date('YmdHis'),
1671  'MEMBER' => $HB_ENV['Uid'],
1672  'USERKEY' => $apptoken,
1673  'TOKEN' => $pass['Token'] );
1674 
1675  if (is_array($mblfrom['data']) || is_array($mblpay['data'])) {
1676  $reply_arr['MP_ACCTLIST'] = array();
1677 
1678  if (is_array($mblfrom['data'])) {
1679  $reply_arr['MP_ACCTLIST']['SOURCEACCTS'] = array();
1680  foreach ($mblfrom['data'] as $akey => $account) {
1681  $acctdet = array();
1682  foreach ($account as $key => $value) {
1683  if ($key == 'AdditionalInfo') {
1684  $acctdet['AdditionalInfo'] = htmlentities(http_build_query($value));
1685  } elseif ($key == 'Name') {
1686  $acctdet['Name'] = htmlentities($value);
1687  } else {
1688  // $key = strtoupper($key);
1689 // $xmlResp .= "<$key>$value</$key>\n";
1690  $acctdet[$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1691  }
1692  }
1693  $reply_arr['MP_ACCTLIST']['SOURCEACCTS'][]['Account'] = $acctdet;
1694  }
1695  }
1696  if (is_array($mblpay['data'])) {
1697  $reply_arr['MP_ACCTLIST']['DESTACCTS'] = array();
1698  foreach ($mblpay['data'] as $akey => $account) {
1699  $acctdet = array();
1700  foreach ($account as $key => $value) {
1701  if ($key == 'AdditionalInfo') {
1702  $acctdet['AdditionalInfo'] = htmlentities(http_build_query($value));
1703  } elseif ($key == 'Name') {
1704  $acctdet['Name'] = htmlentities($value);
1705  } else {
1706  // $key = strtoupper($key);
1707 // $xmlResp .= "<$key>$value</$key>\n";
1708  $acctdet[$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1709  }
1710  }
1711  $reply_arr['MP_ACCTLIST']['DESTACCTS'][]['Account'] = $acctdet;
1712  }
1713  }
1714  }
1715 
1716  break;
1717  case "MP_GETPAYMENTDATES":
1718  $mblpay = bpGetPaymentDates($parms);
1719 
1720  # deal with response
1721  if ($mblpay['status']['response'] == 'false') {
1722  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY DATES
1723  }
1724 
1725  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1726  'DTSERVER' => date('YmdHis'),
1727  'MEMBER' => $HB_ENV['Uid'],
1728  'USERKEY' => $apptoken,
1729  'TOKEN' => $pass['Token'] );
1730 
1731  if (is_array($mblpay['data'])) {
1732  $reply_arr['MP_GETPAYMENTDATES'] = array();
1733  foreach ($mblpay['data'] as $pkey => $paydate) {
1734  switch ($pkey) {
1735  case 'RushOptions':
1736  if (is_array($paydate)) {
1737  $det_arr = array();
1738  foreach ($paydate as $dateitem) {
1739  if (is_array($dateitem)) {
1740  $det_arr['Option'] = array();
1741  foreach ($dateitem as $key => $value) {
1742 // $xmlResp .= "<$key>$value</$key>\n";
1743  $det_arr['Option'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1744  }
1745  }
1746  }
1747  $reply_arr['MP_GETPAYMENTDATES'][]['RushOptions'] = $det_arr;
1748  }
1749  break;
1750  case 'PaymentDates':
1751  if (is_array($paydate)) {
1752  $det_arr = array();
1753  foreach ($paydate as $dateitem) {
1754  if (is_array($dateitem)) {
1755  $det_arr['Date'] = array();
1756  foreach ($dateitem as $key => $value) {
1757 // $xmlResp .= "<$key>$value</$key>\n";
1758  $det_arr['Date'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1759  }
1760  }
1761  $reply_arr['MP_GETPAYMENTDATES'][]['PaymentDates'] = $det_arr;
1762  }
1763  }
1764  break;
1765  default:
1766  $reply_arr['MP_GETPAYMENTDATES'][$pkey] = $paydate;
1767  break;
1768  }
1769  }
1770  }
1771 
1772  break;
1773  case "MP_HISTORY":
1774  $mblpay = bpHist($parms);
1775 
1776  # deal with response
1777  if ($mblpay['status']['response'] == 'false') {
1778  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1779  }
1780  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1781  'DTSERVER' => date('YmdHis'),
1782  'MEMBER' => $HB_ENV['Uid'],
1783  'USERKEY' => $apptoken,
1784  'TOKEN' => $pass['Token'] );
1785 
1786  if (is_array($mblpay['data'])) {
1787  $reply_arr['MP_HISTORY'] = array();
1788  foreach ($mblpay['data'] as $pkey => $payment) {
1789  $det_arr = array();
1790  foreach ($payment as $key => $value) {
1791  if ($key == 'AdditionalInfo') {
1792  $det_arr[$key] = htmlentities(http_build_query($value));
1793  } else {
1794  $det_arr[$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1795  }
1796  }
1797  $reply_arr['MP_HISTORY'][]['Payment'] = $det_arr;
1798  }
1799  }
1800 
1801  break;
1802  case "MP_GETRUSHOPTIONS":
1803  $mblpay = bpGetRushOptions($parms);
1804 
1805  # deal with response
1806  if ($mblpay['status']['response'] == 'false') {
1807  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1808  }
1809  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1810  'DTSERVER' => date('YmdHis'),
1811  'MEMBER' => $HB_ENV['Uid'],
1812  'USERKEY' => $apptoken,
1813  'TOKEN' => $pass['Token'] );
1814 
1815  if (is_array($mblpay['data'])) {
1816  $reply_arr['MP_GETRUSHOPTIONS'] = array();
1817  foreach ($mblpay['data'] as $key => $value) {
1818  $reply_arr['MP_GETRUSHOPTIONS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1819  }
1820  }
1821 
1822  break;
1823 
1824  case "MP_SCHEDULED":
1825  $mblpay = bpSched($parms);
1826 
1827  # deal with response
1828  if ($mblpay['status']['response'] == 'false') {
1829  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1830  }
1831  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1832  'DTSERVER' => date('YmdHis'),
1833  'MEMBER' => $HB_ENV['Uid'],
1834  'USERKEY' => $apptoken,
1835  'TOKEN' => $pass['Token'] );
1836 
1837  if (is_array($mblpay['data'])) {
1838  $reply_arr['MP_SCHEDULED'] = array();
1839  foreach ($mblpay['data'] as $pkey => $payment) {
1840  $det_arr = array();
1841  foreach ($payment as $key => $value) {
1842  if ($key == 'AdditionalInfo') {
1843  $det_arr[$key] = htmlentities(http_build_query($value));
1844  } else {
1845  $det_arr[$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1846  }
1847  }
1848  $reply_arr['MP_SCHEDULED'][]['Payment'] = $det_arr;
1849  }
1850  }
1851 
1852  break;
1853 
1854  case "MP_PAYMENT":
1855  $mblpay = bpPmtAdd($parms);
1856 
1857  # deal with response
1858  if ($mblpay['status']['response'] == 'false') {
1859  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1860  }
1861  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1862  'DTSERVER' => date('YmdHis'),
1863  'MEMBER' => $HB_ENV['Uid'],
1864  'USERKEY' => $apptoken,
1865  'TOKEN' => $pass['Token'] );
1866 
1867  if (is_array($mblpay['data'])) {
1868  $reply_arr['MP_PAYMENT'] = array();
1869  foreach ($mblpay['data'] as $key => $value) {
1870  $reply_arr['MP_PAYMENT'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1871  }
1872  }
1873 
1874  break;
1875 
1876  case "MP_TRANSFER":
1877  $mblpay = bpTrnAdd($parms);
1878 
1879  # deal with response
1880  if ($mblpay['status']['response'] == 'false') {
1881  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1882  }
1883  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1884  'DTSERVER' => date('YmdHis'),
1885  'MEMBER' => $HB_ENV['Uid'],
1886  'USERKEY' => $apptoken,
1887  'TOKEN' => $pass['Token'] );
1888 
1889  if (is_array($mblpay['data'])) {
1890  $reply_arr['MP_TRANSFER'] = array();
1891  foreach ($mblpay['data'] as $key => $value) {
1892  $reply_arr['MP_TRANSFER'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1893  }
1894  }
1895 
1896  break;
1897 
1898  case "MP_EDITPMT":
1899  $mblpay = bpPmtEdit($parms);
1900 
1901  # deal with response
1902  if ($mblpay['status']['response'] == 'false') {
1903  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1904  }
1905  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1906  'DTSERVER' => date('YmdHis'),
1907  'MEMBER' => $HB_ENV['Uid'],
1908  'USERKEY' => $apptoken,
1909  'TOKEN' => $pass['Token'] );
1910 
1911  if (is_array($mblpay['data'])) {
1912  $reply_arr['MP_EDITPMT'] = array();
1913  foreach ($mblpay['data'] as $key => $value) {
1914  $reply_arr['MP_EDITPMT'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1915  }
1916  }
1917 
1918  break;
1919 
1920  case "MP_STOPPMT":
1921  $mblpay = bpPmtStop($parms);
1922 
1923  # deal with response
1924  if ($mblpay['status']['response'] == 'false') {
1925  throw new Exception($mblpay['status']['message'], $mblpay['status']['code']); # MOBLPAY HIST
1926  }
1927  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1928  'DTSERVER' => date('YmdHis'),
1929  'MEMBER' => $HB_ENV['Uid'],
1930  'USERKEY' => $apptoken,
1931  'TOKEN' => $pass['Token'] );
1932 
1933  if (is_array($mblpay['data'])) {
1934  $reply_arr['MP_STOPPMT'] = array();
1935  foreach ($mblpay['data'] as $key => $value) {
1936  $reply_arr['MP_STOPPMT'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
1937  }
1938  }
1939 
1940  break;
1941 
1942  default:
1943  # unknown MP_ACTION
1944  throw new Exception("Unknown MOBLPAY Mode",15550);
1945  break;
1946  }
1947  break;
1948  case "MESSAGE":
1949  # before we get started, check permissions
1950  $accessRights = Perm_AccessRights( $dbh, $HB_ENV, array( "feature" => FEATURE_SECURE_MSG ) );
1951  if ( ! HCU_array_key_value('access', $accessRights)) {
1952  throw new Exception( $HB_ENV['MC']->msg('Rights not set', HCU_DISPLAY_AS_HTML), 915 );
1953  }
1954 
1955 
1956  $pass = array();
1957  parse_str($PASSWITH, $pass);
1958  if (!isset($pass['MSG_SERVICE'])) {
1959  throw new Exception( "Missing Message parameters",4001);
1960  }
1961  $HB_ENV['HCUPOST'] = $pass;
1962  if (HCU_array_key_exists('filter',$pass)) {
1963  $HB_ENV['HCUPOST']['what'] = HCU_array_key_value('filter',$pass);
1964  }
1965  if (HCU_array_key_exists('thread_id',$pass)) {
1966  $HB_ENV['HCUPOST']['parentid'] = HCU_array_key_value('thread_id',$pass);
1967  }
1968 
1969  switch ($pass['MSG_SERVICE']) {
1970  case 'MSGECO':
1971  # this file has not been converted to Odyssey
1972  # responses recoded to send response from arrays in
1973  # new standard format, BUT NOT TESTED YET
1974  # moving on to login issues instead
1975  require_once(dirname(__FILE__) . '/../library/msgECO.i');
1976  break;
1977  default:
1978  throw new Exception("Invalid Message Service",4001);
1979  break;
1980  }
1981  switch ($pass['MSG_ACTION']) {
1982  case "READ_MSGS":
1983  $msgResp = msgReadMessages($dbh, $HB_ENV);
1984 
1985  # deal with response
1986  if ($msgResp['code'] !== '000') {
1987  throw new Exception($msgResp['homecuErrors'], $msgResp['code']); # MESSAGE READ_MSGS
1988  }
1989 
1990 // $HB_ENV["SYSENV"]["logger"]->info(__LINE__ . " msgReadMessages returns " . print_r($msgResp,true));
1991 
1992  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
1993  'DTSERVER' => date('YmdHis'),
1994  'MEMBER' => $HB_ENV['Uid'],
1995  'USERKEY' => $apptoken );
1996  if (is_array($msgResp['homecuData'])) {
1997  $reply_arr['READ_MSGS'] = array();
1998  foreach ($msgResp['homecuData'] as $key => $value) {
1999  if (is_array($value)) {
2000  $msg_arr = array();
2001  foreach ($value as $mkey => $mvalue) {
2002  switch ($mkey) {
2003  case 'subject':
2004  $mvalue = htmlentities($mvalue, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2005  break;
2006  }
2007  $msg_arr[$mkey] = $mvalue;
2008  }
2009  $reply_arr['READ_MSGS'][]['MSG'] = $msg_arr;
2010  } else {
2011  $reply_arr['READ_MSGS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2012  }
2013  }
2014  }
2015 
2016  break;
2017 
2018  case "READ_THREAD":
2019  $msgResp = msgReadMessageThread($dbh, $HB_ENV);
2020 
2021  # deal with response
2022  if ($msgResp['code'] !== '000') {
2023  throw new Exception($msgResp['homecuErrors'], $msgResp['code']); # MESSAGE READ_THREAD
2024  }
2025  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2026  'DTSERVER' => date('YmdHis'),
2027  'MEMBER' => $HB_ENV['Uid'],
2028  'USERKEY' => $apptoken );
2029 
2030  if (is_array($msgResp['homecuData'])) {
2031  $reply_arr['READ_THREAD'] = array();
2032  foreach ($msgResp['homecuData'] as $key => $value) {
2033  if (is_array($value)) {
2034  $msg_arr = array();
2035  foreach ($value as $mkey => $mvalue) {
2036  switch ($mkey) {
2037  case 'subject':
2038  case 'message':
2039  $mvalue = htmlentities($mvalue, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2040  break;
2041  }
2042  $msg_arr[$mkey] = $mvalue;
2043  }
2044  $reply_arr['READ_THREAD'][]['MSG'] = $msg_arr;
2045  } else {
2046  $reply_arr['READ_THREAD'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2047  }
2048  }
2049  }
2050 
2051  break;
2052 
2053  case "SEND_MSG":
2054  $msgResp = msgSendMessage($dbh, $HB_ENV, $MC);
2055 
2056  # deal with response
2057  if ($msgResp['code'] !== '000') {
2058  throw new Exception($msgResp['homecuErrors'], $msgResp['code']); # MESSAGE SEND_MSG
2059  }
2060  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2061  'DTSERVER' => date('YmdHis'),
2062  'MEMBER' => $HB_ENV['Uid'],
2063  'USERKEY' => $apptoken );
2064  # 11/14/17 msgSendMessage return calls msgReadMessages, returning a list of messages
2065  # (and effectively hiding any read failure as a send failure.) For now, if we got
2066  # a successful result, assume the send succeeded and ignore the message list
2067  # returning 'success', as Mammoth did, so apps don't have to change arbitrarily
2068  $reply_arr['SEND_MSG'] = 'success';
2069 
2070  break;
2071 
2072  case "DEL_THREAD":
2073  $msgResp = msgDeleteMessageThread($dbh, $HB_ENV);
2074 
2075  # deal with response
2076  if ($msgResp['code'] !== '000') {
2077  throw new Exception($msgResp['homecuErrors'], $msgResp['code']); # MESSAGE DEL_THREAD
2078  }
2079  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2080  'DTSERVER' => date('YmdHis'),
2081  'MEMBER' => $HB_ENV['Uid'],
2082  'USERKEY' => $apptoken );
2083  # 11/14/17 msgDeleteMessageThread return calls msgReadMessages, returning a list of messages
2084  # (and effectively hiding any read failure as a delete failure.) For now, if we got
2085  # a successful result, assume the delete succeeded and ignore the message list
2086  # returning 'success', as Mammoth did, so apps don't have to change arbitrarily
2087  $reply_arr['DEL_MSG'] = 'success';
2088 
2089  break;
2090 
2091  case "CHECK_MSG":
2092  $msgResp = msgCheckForMessages($dbh, $HB_ENV);
2093 
2094  # deal with response
2095  if ($msgResp['code'] !== '000') {
2096  throw new Exception($msgResp['homecuErrors'], $msgResp['code']); # MESSAGE READ_MSGS
2097  }
2098  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2099  'DTSERVER' => date('YmdHis'),
2100  'MEMBER' => $HB_ENV['Uid'],
2101  'USERKEY' => $apptoken );
2102 
2103  if (is_array($msgResp['homecuData'])) {
2104  $reply_arr['CHECK_MSG'] = array();
2105  foreach ($msgResp['homecuData'] as $key => $value) {
2106  $reply_arr['CHECK_MSG'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2107  }
2108  }
2109 
2110  break;
2111 
2112  default:
2113  # unknown MSG_ACTION
2114  throw new Exception("Unknown MESSAGE Mode",15550);
2115  break;
2116  }
2117  break;
2118 
2119  case "RDCMOBILE":
2120  # check permissions in plugin function
2121  require_once(dirname(__FILE__) . '/../library/rdcCommon.i'); # HCU functions common to all RDC vendors
2122 
2123 
2124  $pass = array();
2125  // note! this is an extra decode for all _ACTION settings other than UPLOAD. Because UPLOAD does an
2126  // extra encode on the app side. Didn't want to disrupt deployed apps, and extra decode is benign since
2127  // none of the other PASSWITH values for RDCMOBILE allow special characters. This will bite us someday...
2128  // but is expedient today.
2129  parse_str(urldecode($PASSWITH), $pass);
2130  if ( !isset($pass['MBRACCT'] )) {
2131  throw new Exception("Missing RDC parameters",4001);
2132  }
2133 
2134  // errors will come back as a status code and message
2135  // success will come back as the structure AppFeed expects
2136  $result = HandleRDCRequest( $HB_ENV, $pass );
2137  if ( isset( $result["STATUS"]["CODE"] ) && ( $result["STATUS"]["CODE"] != 0 ) ) {
2138  throw new Exception($result["STATUS"]["MESSAGE"], $result["STATUS"]["CODE"]);
2139  }
2140 
2141  // return the result (should work as is returned)
2142  $reply_arr = array('STATUS' => array('CODE' => 0),
2143  'DTSERVER' => date('YmdHis'),
2144  'MEMBER' => $HB_ENV['Uid'],
2145  'MBRACCT' => $pass['MBRACCT'],
2146  'USERKEY' => $apptoken,
2147  "RDC_RESPONSE" => $result["RDC_RESPONSE"] );
2148 
2149  if ( HCU_array_key_exists("DEPOSITID", $result) ) {
2150  $reply_arr["DEPOSITID"] = $result["DEPOSITID"];
2151  }
2152 
2153  break;
2154  case "ESTMT":
2155  # before we get started, check permissions
2156  $accessRights = Perm_AccessRights( $dbh, $HB_ENV, array( "feature" => FEATURE_ESTATEMENTS ) );
2157  if ( ! HCU_array_key_value('access', $accessRights)) {
2158  throw new Exception( $HB_ENV['MC']->msg('Rights not set', HCU_DISPLAY_AS_HTML), 915 );
2159  }
2160 
2161  # PASSWITH ES_UPD=1 indicates app supports estmt_flag setting changes
2162  # response set includes terms & notices
2163  # PASSWITH ENROLL=START/STOP updates status, and returned response set
2164  # contains status & enrollment block only
2165  # PASSWITH MBRACCT=select member account
2166 
2167  $pass = array();
2168  parse_str($PASSWITH, $pass);
2169  # must indicate member account
2170  if ( empty($pass['MBRACCT']) ) {
2171  throw new Exception("Missing eStatement parameters",4050); # ESTMT
2172  }
2173 
2174  if (isset($pass['ENROLL'])) {
2175  /*
2176  * set Cu Cn Ml esProcessMode as START/STOP and if STOP, HCUPOST['stop_reason']
2177  * then call Post_CUEStmt to change estatement status
2178  * and Update_NoticeInfo to record
2179  * errors on the Update_NoticeInfo are ignored - small likelihood
2180  * returned message set is STATUS & ENROLLMENT only
2181  */
2182 
2183  $HB_ENV['esProcessMode'] = $pass['ENROLL'];
2184 
2185  if ($pass["ENROLL"] == "STOP") {
2186  $HB_ENV['HCUPOST']['stop_reason'] = $pass['STOP_REASON'];
2187  }
2188 
2189 
2190  # Post_CUEstmt expects un-encoded $Ml in HB_ENV array
2191  # app stores it encoded, so decode it before calling the function
2192  # and then put it back after so nothing else breaks
2193 // $HB_ENV['Ml'] = urldecode($HB_ENV['Ml']);
2194  $response = Post_CUEStmt($dbh, $HB_ENV, $MC, $pass['MBRACCT']);
2195 // $HB_ENV['Ml'] = urlencode($HB_ENV['Ml']);
2196  if (count($response['status']['errors']) > 0) {
2197  throw new Exception(implode(' ',$response['status']['errors']),15592); # Post_CUEStmt
2198  } else {
2199  if (intval($pass['NOTICE_ID']) > 0) {
2200  // build up a response to the notice so the member doesn't see again
2201  $HB_ENV["HCUPOST"]["notice_type"] = "C";
2202  $HB_ENV["HCUPOST"]["notice_id"] = intval($pass['NOTICE_ID']);
2203  $HB_ENV["HCUPOST"]["notice_device"] = "P"; // app
2204  $HB_ENV["HCUPOST"]["notice_response"] = array("answer" => 1); // something non-zero but numeric
2205  $HB_ENV["HCUPOST"]["notice_cancel"] = "0";
2206  $HB_ENV["HCUPOST"]["notice_accountnumber"] = $pass["MBRACCT"];
2207 
2208  $response = Update_NoticeInfo($dbh, $HB_ENV, $MC);
2209  // if (count($response['status']['errors']) > 0) {
2210  // throw new Exception(implode(' ',$response['status']['errors']),15593); # Update_NoticeInfo ES Status change
2211  // }
2212  }
2213 
2214  $response = Get_Estmt($dbh, $HB_ENV, $MC, $pass['MBRACCT'], 1);
2215  }
2216  } else {
2217 
2218  $response = Get_Estmt($dbh, $HB_ENV, $MC, $pass['MBRACCT'] );
2219  }
2220  # deal with response
2221  if (HCU_array_item_count('errors',$response['status']) > 0) {
2222  throw new Exception(implode(' ',$response['status']['errors']),15590); # Get_Estmt
2223  } else {
2224  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2225  'DTSERVER' => date('YmdHis'),
2226  'MEMBER' => $HB_ENV['Uid'],
2227  'MBRACCT' => $pass['MBRACCT'],
2228  'USERKEY' => $apptoken );
2229  $xmlResp = '';
2230 
2231  foreach ($response['estmt'] as $key => $value) {
2232  switch (strtolower($key)) {
2233  case "enrolled":
2234  $enrolled = $value;
2235  $key = strtoupper($key);
2236  $reply_arr[$key] = $value;
2237  break;
2238  case "pdflinks":
2239  $reply_arr['PDFLINKS'] = array();
2240  if (is_array($value)) {
2241  foreach ($value as $tag => $tval) {
2242  $det_arr = array();
2243  foreach ($tval as $tkey => $tkval) {
2244  $tkey = strtoupper($tkey);
2245  $det_arr[$tkey] = $tkval;
2246  }
2247  $reply_arr['PDFLINKS'][]['PDFLINK'] = $det_arr;
2248  }
2249  }
2250  break;
2251  case "toclinks":
2252  $reply_arr['TOCLINKS'] = array();
2253  if (is_array($value)) {
2254  foreach ($value as $tag => $tval) {
2255  $det_arr = array();
2256  foreach ($tval as $tkey => $tkval) {
2257  $tkey = strtoupper($tkey);
2258  $det_arr[$tkey] = $tkval;
2259  }
2260  $reply_arr['TOCLINKS'][]['TOCLINK'] = $det_arr;
2261  }
2262  }
2263  break;
2264  }
2265  }
2266 # add appropriate messages -
2267 // if ES_UPD use Get_NoticeInfo else use hardcoded values
2268 
2269  if (isset($pass['ES_UPD'])) {
2270  # set new ENROLLMENT block
2271  if ($enrolled == "W") {
2272  // give message about waiting for first statement and allow to stop
2273  $termsMsg = $MC->msg('Statement Not Found') . " " . $MC->msg('Statement Missing') . " " . $MC->msg("Statements Stop");
2274  $start = "stop";
2275  $termsName = "esTermsStop";
2276  $termsTitle = $MC->msg("Stop e-Statements");
2277  } else if ($enrolled == "Y") {
2278  $termsMsg = $MC->msg("Statements Stop");
2279  $start = "stop";
2280  $termsName = "esTermsStop";
2281  $termsTitle = $MC->msg("Stop e-Statements");
2282  } else {
2283  $termsMsg = $MC->msg('Account not set for EStatements') . ". " . $MC->msg('Sign up fast');
2284  $start = "start";
2285  $termsName = "esTermsStart";
2286  $termsTitle = $MC->msg("Start e-Statements");
2287  }
2288  $reply_arr['ENROLLMENT'] = array(
2289  'ENROLLED' => $enrolled,
2290  'TERMS_TITLE' => $termsTitle,
2291  'TERMS_MSG' => $termsMsg,
2292  'TERMS_BTN' => $MC->msg('Click Here'));
2293 
2294  $response = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', $termsName);
2295  // make sure we got the notice
2296  if ($response['status']['code'] != "000" ||
2297  !count($response['notice']) ||
2298  count($response['status']['errors']) > 0 ||
2299  strlen($response['notice'][0]['notice_text']) == 0) {
2300  $response['status']['errors'][] = "Error retrieving Terms of Use";
2301  throw new Exception(implode(' ',$response['status']['errors']),15593); # Get_NoticeInfo estatement terms
2302  } else {
2303  if (count($response['notice'])) {
2304  foreach ($response['notice'] as $noticekey => $details) {
2305  $reply_arr['TERMS'] = array();
2306  /*
2307  * if no answer list but I have a donotshowtext, send that as an answer
2308  */
2309  if (!count($details['notice_answers']) && sizeof($details['notice_donotshowtext'])) {
2310  $details['notice_answers'][] = array('answer_id' => $details['notice_id'], 'answer_text' => $details['notice_donotshowtext']);
2311  $details['notice_answertype'] = 'M';
2312  }
2313  foreach ($details as $key => $value) {
2314  $key = strtoupper($key);
2315  switch ($key) {
2316  case "NOTICE_ANSWERS":
2317  $reply_arr['ENROLLMENT']['TERMS']['NOTICE_ANSWERS'] = array();
2318  foreach ($value as $anskey => $ansdetl) {
2319  $reply_arr['ENROLLMENT']['TERMS']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2320  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2321  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2322  }
2323 
2324  break;
2325  /*
2326  * ignore these
2327  */
2328  case "NOTICE_INTRO":
2329  case "NOTICE_TITLE":
2330  case "NOTICE_DONOTSHOWTEXT":
2331  case "NOTICE_MSG_TX":
2332  case "NOTICE_MSG_TX_SHOW":
2333  case "NOTICE_MSG_TX_PERM":
2334  case "NOTICE_SUPPRESSRESPONSE":
2335  case "NOTICE_POSTTARGET":
2336  case "NOTICE_TEXT":
2337  break;
2338  case "NOTICE_TYPE":
2339  case "NOTICE_ID":
2340  case "NOTICE_POPUP":
2341  case "NOTICE_ANSWERTYPE":
2342  case "NOTICE_LINKTARGET":
2343  case "NOTICE_LINKDISPLAY":
2344  default:
2345 
2346  $reply_arr['ENROLLMENT']['TERMS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2347  }
2348  }
2349  }
2350  } else {
2351  $reply_arr['ENROLLMENT']['TERMS'] = array();
2352  }
2353  }
2354  } else {
2355  switch ($response['estmt']['enrolled']) {
2356  case 'Y':
2357  $reply_arr['MESSAGES'][]['MESSAGE'] = array('MSGHEAD' => 'Discontinuing eStatement Service',
2358  'MSGBODY' => 'Please log in through the full Home Banking site to discontinue eStatement service');
2359  break;
2360  case 'W':
2361  $reply_arr['MESSAGES'][]['MESSAGE'] = array('MSGHEAD' => $MC->msg('Statement Not Found'),
2362  'MSGBODY' => $MC->msg('Statement Missing'));
2363  break;
2364  case 'N':
2365  default:
2366  $reply_arr['MESSAGES'][]['MESSAGE'] = array('MSGHEAD' => 'Enrollment Required',
2367  'MSGBODY' => 'Please log in through the full Home Banking site to sign up for eStatement');
2368  break;
2369  }
2370  }
2371 
2372 # now get the estatement notice, if any
2373  $txnotice = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'esNotice', 0);
2374 
2375  if (count($txnotice['notice'])) {
2376  foreach ($txnotice['notice'] as $noticekey => $details) {
2377  /*
2378  * if no answer list but I have a donotshowtext, send that as an answer
2379  */
2380  if (!count($details['notice_answers']) && sizeof($details['notice_donotshowtext'])) {
2381  $details['notice_answers'][] = array('answer_id' => $details['notice_id'], 'answer_text' => $details['notice_donotshowtext']);
2382  $details['notice_answertype'] = 'M';
2383  }
2384 
2385  foreach ($details as $key => $value) {
2386  $key = strtoupper($key);
2387  switch ($key) {
2388  case "NOTICE_ANSWERS":
2389  $reply_arr['NOTICE']['NOTICE_ANSWERS'] = array();
2390  foreach ($value as $anskey => $ansdetl) {
2391  $reply_arr['NOTICE']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2392  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2393  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2394  }
2395 
2396  break;
2397 
2398  /*
2399  * ignore these
2400  */
2401  case "NOTICE_INTRO":
2402  case "NOTICE_TITLE":
2403  case "NOTICE_DONOTSHOWTEXT":
2404  case "NOTICE_MSG_TX":
2405  case "NOTICE_MSG_TX_SHOW":
2406  case "NOTICE_MSG_TX_PERM":
2407  case "NOTICE_SUPPRESSRESPONSE":
2408  case "NOTICE_POSTTARGET":
2409  case "NOTICE_TEXT":
2410  break;
2411  case "NOTICE_TYPE":
2412  case "NOTICE_ID":
2413  case "NOTICE_POPUP":
2414  case "NOTICE_ANSWERTYPE":
2415  case "NOTICE_LINKTARGET":
2416  case "NOTICE_LINKDISPLAY":
2417  default:
2418 
2419  $reply_arr['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2420  }
2421  }
2422  }
2423  } else {
2424  $reply_arr['NOTICE'] = array();
2425  }
2426 
2427  }
2428 
2429  break;
2430  case "GETALERTS":
2431  $pass = array();
2432  parse_str($PASSWITH, $pass);
2433 #is cu configured for Alerts?
2434  if (!Check_AlertsEnabled($dbh, $HB_ENV)) {
2435  throw new Exception("Alert feature not configured",3180); # no 'Alert from' email set in admin
2436  }
2437 
2438 #list of alert types
2439  $ary_alerttypes = Get_AlertTypes($MC);
2440 #list of cell phone providers
2441  $ary_cellproviders = Get_AlertProviders($dbh);
2442 #cuusers.email and most-recently-used cell number for use as default values when defining a new alert
2443  $ary_dfltmail = Get_AlertDefaultEmail($dbh, $HB_ENV);
2444  $ary_dfltcell = Get_AlertDefaultCell($dbh, $HB_ENV);
2445 #list of members' accounts eligible for each alert type
2446 // $ary_acctlist = Get_AlertAccountList($dbh, $HB_ENV['Cu'],$pass['MBRACCT'],$HB_ENV['Uid']);
2447  # FOR ODYSSEY GET LIST OF ACCOUNT NUMBERS, then Get_AlertAccountList for ea. account
2448  $ary_acctlist = array();
2449  $accts = Get_AlertAccountList( $dbh, $HB_ENV );
2450  for ($i = 0; $i < count($accts['data']); $i++) {
2451  $ary_acctlist['data'][] = $accts['data'][$i];
2452  }
2453 
2454 #list of currently-defined alerts, if any
2455  $ary_alertdetails = Get_AlertsDetailed($dbh, $HB_ENV['Cu'],$HB_ENV['Uid'], $HB_ENV['Fset3']);
2456 #alerts terms of use document, if any, and a link to display terms of use on demand
2457  $ary_alertterms = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'alertTerms');
2458 
2459 #a status indicating whether or not the member must accept terms before continuing
2460 #any other alerts messages or notices the member should see
2461  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
2462  'DTSERVER' => date('YmdHis'),
2463  'MEMBER' => $HB_ENV['Uid'],
2464  'USERKEY' => $apptoken,
2465  'ALERTSMRY' => array());
2466 
2467  $reply_arr['ALERTSMRY']['ALERTTYPES'] = array();
2468  foreach ($ary_alerttypes['data'] as $alertarr) {
2469  $det_arr = array();
2470  foreach ($alertarr as $key => $value) {
2471  $key = strtoupper($key);
2472  $det_arr[$key] = htmlspecialchars($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2473  }
2474  $reply_arr['ALERTSMRY']['ALERTTYPES'][]['ALERTTYPE'] = $det_arr;
2475  }
2476 
2477  $reply_arr['ALERTSMRY']['CELLPROVIDERS'] = array();
2478  foreach ($ary_cellproviders['data'] as $cellprovider) {
2479  $det_arr = array();
2480  foreach ($cellprovider as $key => $value) {
2481  $key = strtoupper($key);
2482  $det_arr[$key] = htmlspecialchars($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2483  }
2484  $reply_arr['ALERTSMRY']['CELLPROVIDERS'][]['CELLPROVIDER'] = $det_arr;
2485  }
2486  $reply_arr['ALERTSMRY']['EMAILDFLT'] = htmlspecialchars($ary_dfltmail['data'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2487  foreach ($ary_dfltcell['data'] as $key => $value) {
2488  $key = strtoupper($key);
2489  $reply_arr['ALERTSMRY']['CELLDFLT'][$key] = htmlspecialchars($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2490  }
2491 
2492  $reply_arr['ALERTSMRY']['ACCOUNTS'] = array();
2493  for ( $i = 0; $i < HCU_array_item_count("data", $ary_acctlist); $i++ ) {
2494  $acctarr = $ary_acctlist["data"][$i];
2495 
2496  $det_arr = array();
2497  foreach ($acctarr as $key => $value) {
2498  $key = strtoupper($key);
2499  $det_arr[$key] = htmlspecialchars($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2500  }
2501  $reply_arr['ALERTSMRY']['ACCOUNTS'][]['ACCOUNT'] = $det_arr;
2502  }
2503 
2504  $reply_arr['ALERTSMRY']['ALERTS'] = array();
2505  foreach ($ary_alertdetails['data'] as $detlarr) {
2506  $det_arr = array();
2507  foreach ($detlarr as $key => $value) {
2508  $key = strtoupper($key);
2509  $det_arr[$key] = htmlspecialchars($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2510  }
2511  $reply_arr['ALERTSMRY']['ALERTS'][]['ALERT'] = $det_arr;
2512  }
2513  $reply_arr['ALERTSMRY']['TERMS'] = array();
2514  if (count($ary_alertterms['notice'])) {
2515  foreach ($ary_alertterms['notice'] as $noticekey => $details) {
2516  foreach ($details as $key => $value) {
2517  $key = strtoupper($key);
2518  switch ($key) {
2519  case "NOTICE_ANSWERS":
2520 
2521  $reply_arr['ALERTSMRY']['TERMS']['NOTICE_ANSWERS'] = array();
2522  foreach ($value as $anskey => $ansdetl) {
2523  $reply_arr['ALERTSMRY']['TERMS']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2524  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2525  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2526  }
2527  break;
2528  /*
2529  * ignore these
2530  */
2531  case "NOTICE_INTRO":
2532  case "NOTICE_TITLE":
2533  case "NOTICE_DONOTSHOWTEXT":
2534  case "NOTICE_MSG_TX":
2535  case "NOTICE_MSG_TX_SHOW":
2536  case "NOTICE_MSG_TX_PERM":
2537  case "NOTICE_SUPPRESSRESPONSE":
2538  case "NOTICE_POSTTARGET":
2539  case "NOTICE_TEXT":
2540  break;
2541  case "NOTICE_TYPE":
2542  case "NOTICE_ID":
2543  case "NOTICE_POPUP":
2544  case "NOTICE_ANSWERTYPE":
2545  case "NOTICE_LINKTARGET":
2546  case "NOTICE_LINKDISPLAY":
2547  default:
2548 
2549  $reply_arr['ALERTSMRY']['TERMS'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2550  }
2551  }
2552  }
2553  }
2554  /*
2555  * if we need to allow for alert disclosure doc, this is how we'd do it
2556  #get alerts notice document, if any, and a link to display notice on demand
2557  */
2558  $ary_alertnotice = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'alertNotice');
2559 
2560  if (count($ary_alertnotice['notice'])) {
2561  foreach ($ary_alertnotice['notice'] as $noticekey => $details) {
2562  foreach ($details as $key => $value) {
2563  $key = strtoupper($key);
2564  switch ($key) {
2565  case "NOTICE_ANSWERS":
2566  $reply_arr['ALERTSMRY']['NOTICE']['NOTICE_ANSWERS'] = array();
2567  foreach ($value as $anskey => $ansdetl) {
2568  $reply_arr['ALERTSMRY']['NOTICE']['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2569  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2570  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2571  }
2572 
2573  break;
2574  // ignore these
2575  case "NOTICE_INTRO":
2576  case "NOTICE_TITLE":
2577  case "NOTICE_DONOTSHOWTEXT":
2578  case "NOTICE_MSG_TX":
2579  case "NOTICE_MSG_TX_SHOW":
2580  case "NOTICE_MSG_TX_PERM":
2581  case "NOTICE_SUPPRESSRESPONSE":
2582  case "NOTICE_POSTTARGET":
2583  case "NOTICE_TEXT":
2584  break;
2585  case "NOTICE_TYPE":
2586  case "NOTICE_ID":
2587  case "NOTICE_POPUP":
2588  case "NOTICE_ANSWERTYPE":
2589  case "NOTICE_LINKTARGET":
2590  case "NOTICE_LINKDISPLAY":
2591  default:
2592 
2593  $reply_arr['ALERTSMRY']['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2594  }
2595  }
2596  }
2597  }
2598 
2599  break;
2600 
2601  case "UPDALERT":
2602  $pass = array();
2603  parse_str($PASSWITH, $pass);
2604  $HB_ENV['HCUPOST'] = array(); # start w/ empty array
2605  $HB_ENV['HCUPOST']['type'] = HCU_array_key_value('TYPE',$pass);
2606  $HB_ENV['HCUPOST']['notifymsg'] = HCU_array_key_value('NOTIFYMSG',$pass);
2607  $HB_ENV['HCUPOST']['emailtype'] = HCU_array_key_value('EMAILTYPE',$pass);
2608  $HB_ENV['HCUPOST']['provider_id'] = HCU_array_key_value('PROVIDER_ID',$pass);
2609  $HB_ENV['HCUPOST']['notifyto'] = HCU_array_key_value('NOTIFYTO',$pass);
2610  $HB_ENV['HCUPOST']['id'] = HCU_array_key_value('ALERTID',$pass);
2611  $HB_ENV['HCUPOST']['mbr_account'] = HCU_array_key_value('MBRACCT',$pass);
2612  $HB_ENV['HCUPOST']['selacct'] = HCU_array_key_value('SELACCT',$pass);
2613  $HB_ENV['HCUPOST']['inctransdesc'] = HCU_array_key_value('INCTRANSDESC',$pass);
2614 
2615  switch (strtolower($pass['TYPE'])) {
2616  case 'bal':
2617  $HB_ENV['HCUPOST']['notifyamt'] = HCU_array_key_value('NOTIFYAMT',$pass);
2618  $HB_ENV['HCUPOST']['incbal'] = HCU_array_key_value('INCBAL',$pass);
2619  $HB_ENV['HCUPOST']['useavailbal'] = HCU_array_key_value('USEAVAILBAL',$pass);
2620  case 'trans':
2621  $HB_ENV['HCUPOST']['notifydesc'] = HCU_array_key_value('NOTIFYDESC',$pass);
2622  $HB_ENV['HCUPOST']['userange'] = HCU_array_key_value('USERANGE',$pass);
2623  $HB_ENV['HCUPOST']['desc_amtmin'] = HCU_array_key_value('DESC_AMTMIN',$pass);
2624  $HB_ENV['HCUPOST']['desc_amtmax'] = HCU_array_key_value('DESC_AMTMAX',$pass);
2625  $HB_ENV['HCUPOST']['incbal'] = HCU_array_key_value('INCBAL',$pass);
2626  $HB_ENV['HCUPOST']['incamt'] = HCU_array_key_value('INCAMT',$pass);
2627  $HB_ENV['HCUPOST']['transtype'] = HCU_array_key_value('TRANSTYPE',$pass);
2628  case 'check':
2629  $HB_ENV['HCUPOST']['chknum'] = HCU_array_key_value('CHKNUM',$pass);
2630  $HB_ENV['HCUPOST']['incamt'] = HCU_array_key_value('INCAMT',$pass);
2631  case 'loan':
2632  $HB_ENV['HCUPOST']['days_prior'] = HCU_array_key_value('DAYS_PRIOR',$pass);
2633  break;
2634  default:
2635  throw new Exception('Invalid Alert Update Request',3120); # unrecognized Alert type
2636  break;
2637  }
2638  $validalert = Validate_Alert($dbh, $HB_ENV, $MC);
2639  if ($validalert['code'] != '000') {
2640  throw new Exception(implode(' ',$validalert['errors']),3212); # failed Validate_Alert
2641  } else {
2642  $validalert = Update_Alert($dbh, $HB_ENV, $MC);
2643  if ($validalert['code'] != '000') {
2644  throw new Exception(implode(' ',$validalert['errors']),3214); # failed Update_Alert
2645  } else {
2646  $sendkeys = array('USERKEY' => $apptoken);
2647  $reply_arr = Return_ResponseOK($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $sendkeys, "Alert Update Successful");
2648  }
2649  }
2650  break;
2651  case "DELALERT":
2652  $pass = array();
2653  parse_str($PASSWITH, $pass);
2654  $HB_ENV['HCUPOST'] = array(); # start w/ empty array
2655  $HB_ENV['HCUPOST']['type'] = $pass['TYPE'];
2656  $HB_ENV['HCUPOST']['id'] = $pass['ALERTID'];
2657 
2658  switch (strtolower($pass['TYPE'])) {
2659  case 'bal':
2660  case 'trans':
2661  case 'check':
2662  case 'loan':
2663  break;
2664  default:
2665  throw new Exception('Invalid Alert Delete Request',3216); # unrecognized Alert type
2666  break;
2667  }
2668  $validalert = Delete_Alert($dbh, $HB_ENV, $MC);
2669  if ($validalert['code'] != '000') {
2670  throw new Exception(implode(' ',$validalert['errors']),3218); # failed Delete_Alert
2671  } else {
2672  $sendkeys = array('USERKEY' => $apptoken);
2673  $reply_arr = Return_ResponseOK($inPost['ORG'], $HB_ENV['Cn'], $HB_ENV['Uid'], $sendkeys, "Alert Delete Successful");
2674  }
2675  break;
2676  case "ACTIVITY":
2677  // User Activity
2678 
2679  // get just the info we want all into one parameter list
2680  $pass = array();
2681  $activityVar = array(
2682  "INITIAL" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2683  "PENDING" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2684  "PRIOR" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2685  "DETAIL" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2686  "TXNTYPE" => array('filter' => FILTER_SANITIZE_STRING),
2687  "SCHEDULED" => array('filter' => FILTER_SANITIZE_STRING),
2688  "DTSTART" => array('filter' => FILTER_SANITIZE_STRING),
2689  "DTEND" => array('filter' => FILTER_SANITIZE_STRING),
2690  "ACTION" => array('filter' => FILTER_SANITIZE_STRING)
2691  );
2692 
2693  HCU_ImportVars( $pass, "", $activityVar );
2694 
2695  $detail = !isset( $pass["DETAIL"] ) ? 0 : intval($pass["DETAIL"]);
2696  if ( $detail > 0 ) {
2697  // allow any indication of scheduled txn to mean the txn is scheduled
2698  $isScheduled = isset( $pass["SCHEDULED"] ) && strlen( trim( $pass["SCHEDULED"] ) ) > 0;
2699 
2700  // see if doing an action
2701  $action = isset( $pass["ACTION"] ) ? trim( $pass["ACTION"] ) : "";
2702  if ( strlen( $action ) > 0 ) {
2703  $reply_arr = UserActivityAction( $HB_ENV, $detail, $isScheduled, $action );
2704  } else {
2705  $reply_arr = GatherUserActivityDetail( $HB_ENV, $detail, $isScheduled );
2706  }
2707  } else {
2708  $reply_arr = GatherUserActivity( $HB_ENV, $pass );
2709  }
2710 
2711  // add the userkey
2712  $reply_arr["USERKEY"] = $apptoken;
2713  break;
2714  case "EXTACCTS":
2715  require_once(dirname(__FILE__) . '/../library/hcuExternalAccts.i');
2716 
2717  // check rights
2718  $permissionInputs = array( "feature" => FEATURE_EXTERNAL_TRANSFERS );
2719  $return = Perm_AccessRights( $dbh, $HB_ENV, $permissionInputs );
2720  if ( !$return ) {
2721  // * Rights NOT set up for user access
2722  $aryErrors[] = $MC->msg('Rights not set', HCU_DISPLAY_AS_HTML);
2723  throw new Exception (HCU_JsonEncode($aryErrors));
2724  }
2725 
2726  // External account management - figure out the action
2727  $passedVars = array();
2728  $paramInputs = array(
2729  "ACTION" => array('filter' => FILTER_SANITIZE_STRING)
2730  );
2731 
2732  HCU_ImportArray( $passedVars, $_REQUEST, $paramInputs );
2733 
2734  // make an array of the parameters (need to sanitize)
2735  parse_str($PASSWITH, $pass);
2736 
2737  // add the action to the passed parameters
2738  $pass["action"] = $passedVars["ACTION"];
2739 
2740  // set this flag in case need terms, which means don't do the action
2741  $needsTermsAnswered = false;
2742  $termsInfo = array();
2743  $noticeInfo = array();
2744 
2745  // other parameters depend on the action
2746  switch( $passedVars["ACTION"] ) {
2747  case "get_accounts":
2748  // check if need terms agreed to
2749  $aryExtAcctTerms = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'extAccountTerms');
2750 
2751  if (count($aryExtAcctTerms['notice'])) {
2752  foreach ($aryExtAcctTerms['notice'] as $noticekey => $details) {
2753  foreach ($details as $key => $value) {
2754  $key = strtoupper($key);
2755  switch ($key) {
2756  case "NOTICE_ANSWERS":
2757 
2758  $termsInfo["TERMS"]['NOTICE_ANSWERS'] = array();
2759  foreach ($value as $anskey => $ansdetl) {
2760  $termsInfo["TERMS"]['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2761  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2762  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2763  }
2764  break;
2765  /*
2766  * ignore these
2767  */
2768  case "NOTICE_INTRO":
2769  case "NOTICE_TITLE":
2770  case "NOTICE_DONOTSHOWTEXT":
2771  case "NOTICE_MSG_TX":
2772  case "NOTICE_MSG_TX_SHOW":
2773  case "NOTICE_MSG_TX_PERM":
2774  case "NOTICE_SUPPRESSRESPONSE":
2775  case "NOTICE_POSTTARGET":
2776  case "NOTICE_TEXT":
2777  break;
2778  case "NOTICE_POPUP":
2779  case "NOTICE_TYPE":
2780  case "NOTICE_ID":
2781  case "NOTICE_ANSWERTYPE":
2782  case "NOTICE_LINKTARGET":
2783  case "NOTICE_LINKDISPLAY":
2784  default:
2785  $termsInfo["TERMS"][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2786  }
2787  }
2788  }
2789  }
2790 
2791  // if notice_popup then that means show terms before getting data
2792  $needsTermsAnswered = $termsInfo["TERMS"]["NOTICE_POPUP"] == 1;
2793 
2794  /*
2795  * if we need to allow for alert disclosure doc, this is how we'd do it
2796  #get alerts notice document, if any, and a link to display notice on demand
2797  */
2798  $aryExtAcctNotice = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'extAccountNotice');
2799 
2800  if (count($aryExtAcctNotice['notice'])) {
2801  foreach ($aryExtAcctNotice['notice'] as $noticekey => $details) {
2802  foreach ($details as $key => $value) {
2803  $key = strtoupper($key);
2804  switch ($key) {
2805  case "NOTICE_ANSWERS":
2806  $noticeInfo["NOTICE"]['NOTICE_ANSWERS'] = array();
2807  foreach ($value as $anskey => $ansdetl) {
2808  $noticeInfo["NOTICE"]['NOTICE_ANSWERS'][] = array('ANSWER' => array(
2809  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
2810  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
2811  }
2812 
2813  break;
2814  // ignore these
2815  case "NOTICE_INTRO":
2816  case "NOTICE_TITLE":
2817  case "NOTICE_DONOTSHOWTEXT":
2818  case "NOTICE_MSG_TX":
2819  case "NOTICE_MSG_TX_SHOW":
2820  case "NOTICE_MSG_TX_PERM":
2821  case "NOTICE_SUPPRESSRESPONSE":
2822  case "NOTICE_POSTTARGET":
2823  case "NOTICE_TEXT":
2824  break;
2825  case "NOTICE_TYPE":
2826  case "NOTICE_ID":
2827  case "NOTICE_POPUP":
2828  case "NOTICE_ANSWERTYPE":
2829  case "NOTICE_LINKTARGET":
2830  case "NOTICE_LINKDISPLAY":
2831  default:
2832  $noticeInfo['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2833  }
2834  }
2835  }
2836  }
2837 
2838  $inputFilter = array(
2839  "action" => array('filter' => FILTER_SANITIZE_STRING),
2840  );
2841  break;
2842  case "add_account":
2843  // sanitize the inputs
2844  $inputFilter = array(
2845  "action" => array('filter' => FILTER_SANITIZE_STRING),
2846  "display_name" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
2847  "name_on_account" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
2848  "dfi_routing" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2849  "dfi_account" => array('filter' => FILTER_SANITIZE_STRING),
2850  "dfi_account_type" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
2851  );
2852  break;
2853  case "validate_account":
2854  $inputFilter = array(
2855  "action" => array('filter' => FILTER_SANITIZE_STRING),
2856  "micro1" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2857  "micro2" => array('filter' => FILTER_SANITIZE_NUMBER_INT),
2858  "id" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
2859  );
2860  // sanitize the inputs
2861  break;
2862  case "update_account":
2863  // sanitize the inputs
2864  $inputFilter = array(
2865  "action" => array('filter' => FILTER_SANITIZE_STRING),
2866  "display_name" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
2867  "name_on_account" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
2868  "id" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
2869  );
2870  break;
2871  case "delete_account":
2872  // sanitize the inputs
2873  $inputFilter = array(
2874  "action" => array('filter' => FILTER_SANITIZE_STRING),
2875  "id" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
2876  );
2877  break;
2878  default:
2879  throw new Exception( "Unknown action received: {$passedVars["ACTION"]}", 15511 );
2880  }
2881 
2882  // note: not returning DTSERVER or MEMBER since there is no reason to
2883  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO' ),
2884  'USERKEY' => $apptoken,
2885  'EXTACCT_RESP' => array() );
2886 
2887  // only get the data if don't need terms answered
2888  if ( !$needsTermsAnswered ) {
2889  HCU_ImportArray( $inputVars, $pass, $inputFilter );
2890 
2891  $return = ManageExternalAccount( $HB_ENV, $inputVars );
2892 
2893  // check for an error
2894  if ( $return["status"]["code"] !== "000" ) {
2895  // just return a single error
2896  $returnError = "";
2897  if ( is_array( $return["status"]["errors"] ) ) {
2898  for ( $i = 0; $i < count( $return["status"]["errors"] ); $i++ ) {
2899  if ( strlen( $returnError ) > 0 ) {
2900  $returnError .= " ";
2901  }
2902 
2903  $returnError .= $return["status"]["errors"][$i];
2904  }
2905  } else {
2906  $returnError .= $return["status"]["errors"];
2907  }
2908  throw new Exception ($returnError, 15512);
2909  }
2910 
2911  // return any data based on the action
2912  switch( $passedVars["ACTION"] ) {
2913  case "add_account":
2914  // return the messages as DATA
2915  $returnMessage = "";
2916  for ( $i = 0; $i < count( $return["info"] ); $i++ ) {
2917  if ( strlen( $returnMessage ) > 0 ) {
2918  $returnMessage .= " ";
2919  }
2920  $returnMessage .= $return["info"][$i];
2921  }
2922 
2923  $reply_arr["EXTACCT_RESP"]["DATA"]["MESSAGE"] = $returnMessage;
2924 
2925  // the add_account will return the account list, so fall through to get_accounts!!
2926  case "get_accounts":
2927  $statusLookup = Get_ExternalTransferStatusLookup( $MC );
2928 
2929  for ( $i = 0; $i < count( $return['data'] ); $i++ ) {
2930  $oneAcct = $return["data"][$i];
2931 
2932  $detail = array();
2933 
2934  $detail["ID"] = $oneAcct["id"];
2935  $detail["DISPLAY_NAME"] = htmlspecialchars($oneAcct["display_name"], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2936  $detail["NAME_ON_ACCOUNT"] = htmlspecialchars($oneAcct["name_on_account"], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
2937  $detail["STATUS"] = $oneAcct["status"];
2938  $detail["STATUS_NAME"] = $statusLookup[$oneAcct["status"]];
2939  $detail["REMOTEACCOUNT"] = $oneAcct["remoteAccount"];
2940 
2941  // special handling for the json data
2942  $acctInfo = HCU_JsonDecode( $oneAcct["remote_info"] );
2943 
2944  $type = $acctInfo["rdfi"]["type"] == 10 ? $MC->msg( "ACH Checking" ) : $MC->msg( "ACH Savings" );
2945  $detail["ACCOUNTTYPE"] = $type;
2946 
2947  // verify info depends on status
2948  $verifyInfo = "";
2949  if ( $oneAcct["status"] == "p" ) {
2950  $detail["PENDING_DATE"] = $acctInfo["verify"]["pending_date"];
2951  $detail["TRIES"] = $acctInfo["verify"]["tries"];
2952  } else {
2953  $detail["VERIFIED"] = date( "m/d/Y", $acctInfo["verify"]["verified"] );
2954  }
2955 
2956  $reply_arr['EXTACCT_RESP']['EXTACCTS'][]['ACCT'] = $detail;
2957  }
2958  break;
2959  case "validate_account":
2960  // it is possible for a validate to not work but return an updated retry count
2961  $data = array( "ID" => $return["data"]["id"],
2962  "STATUS" => $return["data"]["status"],
2963  "TRIES" => $return["data"]["tries"],
2964  "MESSAGE" => $return["data"]["message"]
2965  );
2966  $reply_arr["EXTACCT_RESP"]["DATA"] = $data;
2967  break;
2968  case "update_account":
2969  // return the updated content
2970  $data = array( "ID" => $return["data"]["id"],
2971  "NAME_ON_ACCOUNT" => $return["data"]["name_on_account"],
2972  "DISPLAY_NAME" => $return["data"]["display_name"],
2973  "MESSAGE" => $return["info"][0]
2974  );
2975  $reply_arr["EXTACCT_RESP"]["DATA"] = $data;
2976  break;
2977  case "delete_account":
2978  // return the info as a message, since that is what it is
2979  $data = array( "ID" => $return["data"],
2980  "MESSAGE" => $return["info"][0]
2981  );
2982  $reply_arr["EXTACCT_RESP"]["DATA"] = $data;
2983  break;
2984  }
2985  }
2986 
2987  // add any terms
2988  if ( HCU_array_key_exists( "TERMS", $termsInfo ) && HCU_array_key_value( "NOTICE_ID", $termsInfo["TERMS"] ) > 0 ) {
2989  $reply_arr["EXTACCT_RESP"]["TERMS"] = $termsInfo["TERMS"];
2990  }
2991 
2992  // add any notice to the existing array
2993  if ( HCU_array_key_exists( "NOTICE", $noticeInfo ) && HCU_array_key_value( "NOTICE_ID", $noticeInfo["NOTICE"] ) > 0 ) {
2994  $reply_arr["EXTACCT_RESP"]["NOTICE"] = $noticeInfo["NOTICE"];
2995  }
2996 
2997  break;
2998  case "M2MACCTS":
2999  require_once(dirname(__FILE__) . '/../library/hcuExternalAccts.i');
3000 
3001  // check rights
3002  $permissionInputs = array( "feature" => FEATURE_M2M_TRANSFERS );
3003  $return = Perm_AccessRights( $dbh, $HB_ENV, $permissionInputs );
3004  if ( !$return ) {
3005  // * Rights NOT set up for user access
3006  $aryErrors[] = $MC->msg('Rights not set', HCU_DISPLAY_AS_HTML);
3007  throw new Exception (HCU_JsonEncode($aryErrors));
3008  }
3009 
3010  // External account management - figure out the action
3011  $passedVars = array();
3012  $paramInputs = array(
3013  "ACTION" => array('filter' => FILTER_SANITIZE_STRING)
3014  );
3015 
3016  HCU_ImportArray( $passedVars, $_REQUEST, $paramInputs );
3017 
3018  // make an array of the parameters (need to sanitize)
3019  parse_str($PASSWITH, $pass);
3020 
3021  // add the action to the passed parameters
3022  $pass["action"] = $passedVars["ACTION"];
3023 
3024  // set this flag in case need terms, which means don't do the action
3025  $needsTermsAnswered = false;
3026  $termsInfo = array();
3027  $noticeInfo = array();
3028 
3029  // other parameters depend on the action
3030  switch( $passedVars["ACTION"] ) {
3031  case "get_m2m_accounts":
3032  // check if need terms agreed to
3033  $aryM2MAcctTerms = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'm2mAccountTerms');
3034 
3035  if (count($aryM2MAcctTerms['notice'])) {
3036  foreach ($aryM2MAcctTerms['notice'] as $noticekey => $details) {
3037  foreach ($details as $key => $value) {
3038  $key = strtoupper($key);
3039  switch ($key) {
3040  case "NOTICE_ANSWERS":
3041 
3042  $termsInfo["TERMS"]['NOTICE_ANSWERS'] = array();
3043  foreach ($value as $anskey => $ansdetl) {
3044  $termsInfo["TERMS"]['NOTICE_ANSWERS'][] = array('ANSWER' => array(
3045  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
3046  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
3047  }
3048  break;
3049  /*
3050  * ignore these
3051  */
3052  case "NOTICE_INTRO":
3053  case "NOTICE_TITLE":
3054  case "NOTICE_DONOTSHOWTEXT":
3055  case "NOTICE_MSG_TX":
3056  case "NOTICE_MSG_TX_SHOW":
3057  case "NOTICE_MSG_TX_PERM":
3058  case "NOTICE_SUPPRESSRESPONSE":
3059  case "NOTICE_POSTTARGET":
3060  case "NOTICE_TEXT":
3061  break;
3062  case "NOTICE_POPUP":
3063  case "NOTICE_TYPE":
3064  case "NOTICE_ID":
3065  case "NOTICE_ANSWERTYPE":
3066  case "NOTICE_LINKTARGET":
3067  case "NOTICE_LINKDISPLAY":
3068  default:
3069  $termsInfo["TERMS"][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
3070  }
3071  }
3072  }
3073  }
3074 
3075  // if notice_popup then that means show terms before getting data
3076  $needsTermsAnswered = $termsInfo["TERMS"]["NOTICE_POPUP"] == 1;
3077 
3078  /*
3079  * if we need to allow for alert disclosure doc, this is how we'd do it
3080  #get alerts notice document, if any, and a link to display notice on demand
3081  */
3082  $aryM2MAcctNotice = Get_NoticeInfo($dbh, $HB_ENV, $MC, 'P', 'm2mAccountNotice');
3083 
3084  if (count($aryM2MAcctNotice['notice'])) {
3085  foreach ($aryM2MAcctNotice['notice'] as $noticekey => $details) {
3086  foreach ($details as $key => $value) {
3087  $key = strtoupper($key);
3088  switch ($key) {
3089  case "NOTICE_ANSWERS":
3090  $noticeInfo["NOTICE"]['NOTICE_ANSWERS'] = array();
3091  foreach ($value as $anskey => $ansdetl) {
3092  $noticeInfo["NOTICE"]['NOTICE_ANSWERS'][] = array('ANSWER' => array(
3093  'ANSWER_ID' => "ANS_{$ansdetl['answer_id']}",
3094  'ANSWER_TEXT' => htmlentities(CleanWordQuotes($ansdetl['answer_text']), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE)));
3095  }
3096 
3097  break;
3098  // ignore these
3099  case "NOTICE_INTRO":
3100  case "NOTICE_TITLE":
3101  case "NOTICE_DONOTSHOWTEXT":
3102  case "NOTICE_MSG_TX":
3103  case "NOTICE_MSG_TX_SHOW":
3104  case "NOTICE_MSG_TX_PERM":
3105  case "NOTICE_SUPPRESSRESPONSE":
3106  case "NOTICE_POSTTARGET":
3107  case "NOTICE_TEXT":
3108  break;
3109  case "NOTICE_TYPE":
3110  case "NOTICE_ID":
3111  case "NOTICE_POPUP":
3112  case "NOTICE_ANSWERTYPE":
3113  case "NOTICE_LINKTARGET":
3114  case "NOTICE_LINKDISPLAY":
3115  default:
3116  $noticeInfo['NOTICE'][$key] = htmlentities($value, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
3117  }
3118  }
3119  }
3120  }
3121 
3122  $inputFilter = array(
3123  "action" => array('filter' => FILTER_SANITIZE_STRING),
3124  );
3125  break;
3126  case "add_m2m_account":
3127  // sanitize the inputs
3128  $inputFilter = array(
3129  "action" => array('filter' => FILTER_SANITIZE_STRING),
3130  "display_name" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
3131  "name_on_account" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
3132  "dfi_account" => array('filter' => FILTER_SANITIZE_STRING),
3133  "dfi_account_type" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
3134  );
3135  break;
3136  case "update_account":
3137  // sanitize the inputs
3138  $inputFilter = array(
3139  "action" => array('filter' => FILTER_SANITIZE_STRING),
3140  "display_name" => array("filter" => FILTER_SANITIZE_STRING, 'options' => array('flags' => FILTER_FLAG_NO_ENCODE_QUOTES)),
3141  "id" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
3142  );
3143  break;
3144  case "delete_account":
3145  // sanitize the inputs
3146  $inputFilter = array(
3147  "action" => array('filter' => FILTER_SANITIZE_STRING),
3148  "id" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
3149  );
3150  break;
3151  default:
3152  throw new Exception( "Unknown action received: {$passedVars["ACTION"]}", 15511 );
3153  }
3154 
3155  // note: not returning DTSERVER or MEMBER since there is no reason to
3156  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO' ),
3157  'USERKEY' => $apptoken,
3158  'M2MACCT_RESP' => array() );
3159 
3160  // only get the data if don't need terms answered
3161  if ( !$needsTermsAnswered ) {
3162  HCU_ImportArray( $inputVars, $pass, $inputFilter );
3163 
3164  // this function handles both External and M2M accounts
3165  $return = ManageExternalAccount( $HB_ENV, $inputVars );
3166 
3167  // check for an error
3168  if ( $return["status"]["code"] !== "000" ) {
3169  // just return a single error
3170  $returnError = "";
3171  if ( is_array( $return["status"]["errors"] ) ) {
3172  for ( $i = 0; $i < count( $return["status"]["errors"] ); $i++ ) {
3173  if ( strlen( $returnError ) > 0 ) {
3174  $returnError .= " ";
3175  }
3176 
3177  $returnError .= $return["status"]["errors"][$i];
3178  }
3179  } else {
3180  $returnError .= $return["status"]["errors"];
3181  }
3182  throw new Exception ($returnError, 15512);
3183  }
3184 
3185  // return any data based on the action
3186  switch( $passedVars["ACTION"] ) {
3187  case "add_m2m_account":
3188  // return the messages as DATA
3189  $returnMessage = "";
3190  for ( $i = 0; $i < count( $return["info"] ); $i++ ) {
3191  if ( strlen( $returnMessage ) > 0 ) {
3192  $returnMessage .= " ";
3193  }
3194  $returnMessage .= $return["info"][$i];
3195  }
3196 
3197  $reply_arr["M2MACCT_RESP"]["DATA"]["MESSAGE"] = $returnMessage;
3198 
3199  // the add_account will return the account list, so fall through to get_accounts!!
3200  case "get_m2m_accounts":
3201  $statusLookup = Get_ExternalTransferStatusLookup( $MC );
3202 
3203  for ( $i = 0; $i < count( $return['data'] ); $i++ ) {
3204  $oneAcct = $return["data"][$i];
3205 
3206  $detail = array();
3207 
3208  $detail["ID"] = $oneAcct["id"];
3209  $detail["DISPLAY_NAME"] = htmlspecialchars($oneAcct["display_name"], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
3210  $detail["STATUS"] = $oneAcct["status"];
3211  $detail["STATUS_NAME"] = $statusLookup[$oneAcct["status"]];
3212  $detail["REMOTEACCOUNT"] = $oneAcct["remoteAccount"];
3213 
3214  // special handling for the json data
3215  $acctInfo = HCU_JsonDecode( $oneAcct["remote_info"] );
3216 
3217  $type = $acctInfo["rdfi"]["type"] == 10 ? $MC->msg( "ACH Checking" ) : $MC->msg( "ACH Savings" );
3218  $detail["ACCOUNTTYPE"] = $type;
3219 
3220  $detail["VERIFIED"] = date( "m/d/Y", $acctInfo["verify"]["verified"] );
3221 
3222  $reply_arr['M2MACCT_RESP']['M2MACCTS'][]['ACCT'] = $detail;
3223  }
3224  break;
3225  case "update_account":
3226  // return the updated content
3227  $data = array( "ID" => $return["data"]["id"],
3228  "DISPLAY_NAME" => $return["data"]["display_name"],
3229  "MESSAGE" => $return["info"][0]
3230  );
3231  $reply_arr["M2MACCT_RESP"]["DATA"] = $data;
3232  break;
3233  case "delete_account":
3234  // return the info as a message, since that is what it is
3235  $data = array( "ID" => $return["data"],
3236  "MESSAGE" => $return["info"][0]
3237  );
3238  $reply_arr["M2MACCT_RESP"]["DATA"] = $data;
3239  break;
3240  }
3241  }
3242 
3243  // add any terms
3244  if ( HCU_array_key_exists( "TERMS", $termsInfo ) && HCU_array_key_value( "NOTICE_ID", $termsInfo["TERMS"] ) > 0 ) {
3245  $reply_arr["M2MACCT_RESP"]["TERMS"] = $termsInfo["TERMS"];
3246  }
3247 
3248  // add any notice to the existing array
3249  if ( HCU_array_key_exists( "NOTICE", $noticeInfo ) && HCU_array_key_value( "NOTICE_ID", $noticeInfo["NOTICE"] ) > 0 ) {
3250  $reply_arr["M2MACCT_RESP"]["NOTICE"] = $noticeInfo["NOTICE"];
3251  }
3252 
3253  break;
3254  case "GETLANDING":
3255  require_once(dirname(__FILE__) . '/../../shared/library/hcuFunctions.i');
3256 
3257  $passedVars = array();
3258  $paramInputs = array(
3259  "ACTION" => array('filter' => FILTER_SANITIZE_STRING)
3260  );
3261 
3262  HCU_ImportArray( $passedVars, $_REQUEST, $paramInputs );
3263 
3264  $action = strtoupper( $passedVars["ACTION"] );
3265 
3266  switch( $action ) {
3267  case "MENU":
3268  // $passedVars = array();
3269  $paramInputs = array(
3270  "ID" => array('filter' => FILTER_SANITIZE_NUMBER_INT)
3271  );
3272  HCU_ImportArray( $passedVars, $_REQUEST, $paramInputs );
3273  $landingResult = GetLandingMenuTarget( $HB_ENV, $passedVars["ID"], $apptoken );
3274  break;
3275 
3276  case "SSO":
3277  // $passedVars = array();
3278  $paramInputs = array(
3279  "URL" => array('filter' => FILTER_SANITIZE_STRING)
3280  );
3281  HCU_ImportArray( $passedVars, $_REQUEST, $paramInputs );
3282  $landingResult = GetLandingURL( $HB_ENV, $passedVars["URL"], $apptoken );
3283  break;
3284 
3285  default:
3286  // return nothing by default
3287  $landingResult = "";
3288  break;
3289  }
3290 
3291  // return the payload and the landing name
3292  // NOTE: urlencode the target because it has HTML entities that cause issues in XML
3293  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO' ),
3294  'USERKEY' => $apptoken,
3295  'TARGET_RESP' => urlencode( $landingResult )
3296  );
3297 
3298  break;
3299  case "GETCOMPASS":
3300  // call on demand to get the compass menu contents and badge counts
3301 // $menuCompass = GetFakeUserCompassMenu( $HB_ENV );
3302  $menuCompass = function_exists( "CompassMenuJson" ) ? CompassMenuJson( $HB_ENV, $dbh ) : GetFakeUserCompassMenu( $HB_ENV );
3303  $reply_arr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO' ),
3304  'USERKEY' => $apptoken,
3305  'COMPASS' => $menuCompass,
3306  'COMPASS_UPDATE_INTERVAL' => 180 // somewhat arbitrarily chosen
3307  );
3308  break;
3309  default:
3310 # gen OFX
3311 #
3312  $KEYACCTID = HCU_array_key_value('KEYACCTID', $inPost);
3313  $BALONLY = (HCU_array_key_exists('KEYACCTID', $inPost) ? 0 : 1);
3314  if (empty($sendkeys)) { $sendkeys=array(); }
3315 
3316  $reply_arr = fullFeed($dbh, $HB_ENV, $DTSTART, $DTEND, $BALONLY, $SENDAS, $KEYACCTID, $recent, $sendkeys);
3317 
3318  break;
3319  }
3320 
3321  // test to see if being asked for a menu
3322  $userMenuRequest = strtolower(HCU_array_key_value('USERMENU', $inPost));
3323  if ( $userMenuRequest != "" ) {
3324  // add the user menu to the return info
3325  if ( $userMenuRequest == "full" ) {
3326  $userMenu = BuildFullUserMenu($dbh, $HB_ENV, 'A');
3327  } else {
3328  $userMenu = BuildUserMenu($dbh, $HB_ENV, 'A');
3329  }
3330  $reply_arr["usermenu"] = $userMenu;
3331 
3332  // Only do this if the menu is being requested, so it isn't called as often
3333  // add the user profile update info (returns a JSON string)
3334  $updateString = GetUserProfileUpdates( $HB_ENV );
3335  $reply_arr["PROFILE_UPDATES"] = $updateString;
3336  require_once(dirname(__FILE__) . '/../../shared/library/hcuFunctions.i');
3337 
3338  // Only do this if the menu is being requested, so it isn't called as often
3339  $count = GetPendingUserActivity($HB_ENV);
3340  if ( $count > 0 ) {
3341  $reply_arr["USER_ACTIVITY_PENDING"] = $count;
3342  }
3343 
3344  // Only do this if the menu is being requested, so it isn't called as often
3345  // ** Check for the existence of new messages
3346  $count = ReturnUnreadSecureMsg($HB_ENV["dbh"], $HB_ENV);
3347  if ( $count > 0 ) {
3348  $reply_arr["SECURE_MSG_UNREAD"]= $count;
3349  }
3350  }
3351 
3352  // see if need to return encryption key
3353  if ( $appEncryptionKey != "" ) {
3354  $reply_arr["encrkey"] = $appEncryptionKey;
3355  }
3356 
3357  // fall through and return the array
3358 } catch (Exception $e) {
3359  $code = $e->getCode();
3360  $message = $e->getMessage();
3361 
3362  $reply_arr = array('STATUS' => array('CODE' => $code, 'SEVERITY' => 'ERROR'),
3363  'MESSAGE'=> array('ERR' => htmlspecialchars($message, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE), 'ERRNO' => 999, 'ERRSTAT' =>'FAIL'));
3364 
3365 }
3366 
3367  // no more code will exit after this point because send_response() exits.
3368  send_response( $reply_arr, $SENDAS );
3369 
3370 /**
3371  *
3372  * @param string $data XML string to be formatted
3373  * @return string XML 'pretty' with indents
3374  */
3375  function Format_AppFeed($data) {
3376  /*
3377  * decided not to use this -- adds unneccessary white space
3378  * and we want to explore moving to json instead
3379  */
3380 
3381 
3382 
3383 
3384  $dom = new DOMDocument();
3385 
3386  $dom->preserveWhiteSpace = false;
3387  $dom->formatOutput = true;
3388 
3389  $dom->loadXML($data);
3390  $out = $dom->saveXML();
3391 
3392  $out = str_replace('<?xml version="1.0"?>','',$out);
3393 
3394  return ($out);
3395 }
3396 
3397 function send_response($reply_arr, $sendas='XML') {
3398 
3399  switch ($sendas) {
3400  case 'JSON':
3401  $xmlResp = HCU_JsonEncode($reply_arr);
3402  header("Content-Type: application/json");
3403 // header("Content-disposition: inline; filename=\"{$HB_ENV['Cu']}_txns.json\"");
3404  break;
3405  case 'XML':
3406  default:
3407  $xmlResp = Format_AppFeed(assocArrayToXML($reply_arr,'APPFEED'));
3408 // $xmlResp = assocArrayToXML($reply_arr,'APPFEED');
3409  if (HCU_array_item_count('HB_ENV', $GLOBALS) && HCU_array_key_value('Cu',$GLOBALS['HB_ENV'])) {
3410  $replace = GetAppMenu($GLOBALS['HB_ENV']['Cu'],'appconfig.xml');
3411  $xmlResp = preg_replace('/<menu>REPLACE MENU HERE<\/menu>/',"<menu>{$replace}</menu>",$xmlResp);
3412  }
3413 
3414  header("Content-Type: application/x-ofx");
3415 // header("Content-disposition: inline; filename=\"{$HB_ENV['Cu']}_txns.ofx\"");
3416  break;
3417  }
3418 //global $HB_ENV;
3419 //$HB_ENV["SYSENV"]["logger"]->debug( "send_response: " . $xmlResp );
3420 
3421  header("Content-length: " . strlen($xmlResp));
3422  print $xmlResp;
3423 
3424  exit;
3425 }
3426 
3427 function sqlmdy($date) {
3428 
3429  if (strtolower($date) == "now" || strtolower($date) == "today") {
3430  $date = date("Y-m-d");
3431  }
3432  # only allow 0-9 and dash(-) or slash (/)
3433  # also allow dot (.) for milliseconds
3434  if (preg_match("/[^0-9\-\/\.]/", $date)) {
3435  return false;
3436  }
3437  if (preg_match("/[-\/]/", $date)) {
3438  list ($yy, $mm, $dd) = preg_split("/[-\/\.]/", $date);
3439  } else {
3440  $yy = substr($date, 0, 4);
3441  $mm = substr($date, 4, 2);
3442  $dd = substr($date, 6, 2);
3443  }
3444  $mm = sprintf("%02d", intval($mm));
3445  $dd = sprintf("%02d", intval($dd));
3446  if (strlen($yy) > 0 && strlen($yy) < 4) {
3447  $yy = ($yy < 70 ? 2000 + $yy : 1900 + $yy);
3448  }
3449  $yy = sprintf("%04d", intval($yy));
3450  if (checkdate($mm, $dd, $yy)) {
3451  return "${yy}${mm}${dd}";
3452  } else {
3453  return false;
3454  }
3455 }
3456 
3457 function countprint($string) {
3458  print $string;
3459  return strlen($string);
3460 }
3461 
3462 function clockwatch($tz, $CU, $MEMBER) {
3463  $localzone = 'US/Mountain';
3464  $tz = ("$tz" == "" ? "US/Mountain" : $tz);
3465  if (strpos("$tz", "/") === false)
3466  $tz = "US/$tz";
3467  global $dbh;
3468  $sqlll = "set time zone '$tz';
3469  select extract(epoch from CURRENT_TIMESTAMP(0)) -
3470  extract(epoch from substring(lastlogin,1,19)::timestamp)::integer
3471  from cuusers
3472  where cu='$CU' and user_name='$MEMBER';";
3473  $sthcl = db_query($sqlll, $dbh);
3474  if ($sthcl) {
3475  list($sincelast) = db_fetch_array($sthcl, 0);
3476  }
3477  $sthcl = db_query("set time zone '$localzone'", $dbh);
3478  return abs($sincelast);
3479 }
3480 
3481 function MFQ_send_chall($dbh, $HB_ENV, $MC) {
3482 
3483 # sending all questions regardless of '1 random' setting for cu
3484 # updated 9/12 to recognize '1 random' setting
3485  # use odyssey function
3486 
3487  $MemberChallengeQuestions_ary=GetChallengeQuestions("CHALLENGE", $dbh, $HB_ENV, $MC, $HB_ENV['Cn']);
3488 
3489  $reply_arr = array('STATUS' => array('CODE' =>3000,'SEVERITY' => 'ERROR'),
3490  'DTSERVER' => date('YmdHis') );
3491 
3492 // old path $reply_arr['MFACHALLENGETRNRS']['MFACHALLENGERS'] = array();
3493  $reply_arr['MFA'] = array();
3494  $reply_arr['MFA']['AUTHREQ'] = 'MFQ';
3495  $reply_arr['MFA']['MFABUNDLE'] = $HB_ENV['mfaBundle'];
3496 
3497 //# force 'What email' as first challenge question
3498 // $itm_arr = array('MFACHALLENGE' => array(
3499 // 'MFAPHRASEID' => 'MFA_E',
3500 // 'MFAPHRASELABEL' => 'What email address is saved with this account?'));
3501 //
3502 // $reply_arr['MFA'][] = $itm_arr;
3503 
3504 # and now add mfa questions, if any were found
3505  if (count($MemberChallengeQuestions_ary)) {
3506  foreach ((array) $MemberChallengeQuestions_ary as $chakey => $mfaitem) {
3507  $itm_arr = array('MFACHALLENGE' => array(
3508  'MFAPHRASEID' => "MFA_{$mfaitem['cqid']}",
3509  'MFAPHRASELABEL' => "{$mfaitem['display']}"));
3510 
3511  $reply_arr['MFA'][] = $itm_arr;
3512 
3513  }
3514  }
3515 
3516  return $reply_arr;
3517 }
3518 
3519 /**
3520  * Compare MFA challenge question responses to the values saved in the database
3521  *
3522  * @param resource $dbh database handle
3523  * @param array $HB_ENV
3524  * @param array $inPost posted responses
3525  * @return array fail count, fail reason
3526  * @throws Exception
3527  */
3528 function MFQ_response($dbh, $HB_ENV, $inPost) {
3529 
3530  $aryMfaQuest = HCU_JsonDecode($HB_ENV['mfaquest']);
3531  $mbrMfaQuest = HCU_MFADecode($aryMfaQuest); # from the db
3532 
3533  $fail = 0;
3534  $failreason = 0;
3535 
3536  $dbcount = $mbrMfaQuest['mfacount']; # how many questions are in the db?
3537  $mfapost = MFQ_resplist($inPost); # get list of MFA variables in the posted request (skips MFA_E)
3538  # update from mammoth - gets id and answers in array using Odyssey format
3539  $mfacount = count($mfapost); # how many MFA_ responses (excluding MFA_E) did we get?
3540  try {
3541  if ($mfacount < $dbcount && ($HB_ENV['Fset2'] & $GLOBALS['CU2_RANDOM_CHAL']) == 0) {
3542  # expected more challenge questions than we got, so fail
3543  $fail++;
3544  $failreason = $GLOBALS['MEM_LOGIN_FAILED_QST'];
3545  throw new Exception(__LINE__ . 'MFA Failed',$failreason);# expected chall ques and got none
3546 
3547  }
3548  # make sure savemail is set in HB_ENV
3549  # separate eMail check from challenge questions
3550  # eMail check now using isValidEmail from cu_credentials
3551 // if (strtolower($inPost['MFA_E']) !== strtolower($HB_ENV['savemail'])) {
3552 // $fail++;
3553 // $failreason = $GLOBALS['MEM_LOGIN_FAILED_EMAIL'];
3554 // throw new Exception(__LINE__ . 'MFA Failed',$failreason);# email mismatch
3555 // }
3556  /*
3557  * VALIDATE ANSWERS
3558  * IF savecqid has a value and CU configured for only one answer, then ONLY validate that answer
3559  *
3560  * dbcount being greater than one MEANS the aryMfaQuest array contains an 'answers' key, is an array
3561  * and has at least one value
3562  */
3563  /*
3564  * mfaAnswerIdx is set to the list of questions to validate
3565  * this should be:
3566  * if random, and challenge is set, use challenge
3567  * if random and challenge is not set, use the posted response
3568  * if not random, use the db list
3569  */
3570  $aryMfaAnswers = $mbrMfaQuest['answers']; # stored answers
3571  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_RANDOM_CHAL'])) {
3572  if ( $mbrMfaQuest['challenge'] > 0 ) {
3573  // * Stuck on a challenge question
3574  $mfaAnswerIdx = Array($mbrMfaQuest['challenge']);
3575  } else {
3576  // * Not stuck but still random, just check what they posted
3577  $mfaAnswerIdx = array_keys($mfapost);
3578  }
3579  } else {
3580  // * Not random, check all questions from db
3581  $mfaAnswerIdx = array_keys($aryMfaAnswers);
3582  }
3583 
3584  // ** NOW EVALUATE THE ANSWERS
3585  # for each id in $mfaAnserIdx
3586  # intersect should be with posted answer set, not db answer set?
3587 
3588  foreach (array_intersect_key($aryMfaAnswers, array_flip($mfaAnswerIdx)) as $qid => $qanswer) {
3589  if (strtolower(trim($qanswer)) != strtolower(trim(HCU_array_key_value("MFA_$qid",$inPost)))) {
3590  $fail++;
3591  $failreason = $GLOBALS['MEM_LOGIN_FAILED_QST'];
3592 
3593  throw new Exception(__LINE__ . " MFA $qid Failed",$failreason);# chall response mismatch
3594  }
3595  }
3596  } catch (Exception $e) {
3597  # logging handled at point of call - nothing to do here but fall through?
3598  }
3599  return (array($fail, $failreason));
3600 }
3601 
3602 function MFQ_resplist($posted) {
3603  # examines the (sanitized!) array of posted values
3604  # returns list of MFA_ excluding MFA_E (email)
3605  $mfalist = array();
3606  foreach (array_keys($posted) as $rkey) {
3607  $m = strpos($rkey, 'MFA_');
3608  if ($m !== FALSE && $m == 0 && $rkey !== 'MFA_E') {
3609 // $mfalist[] = array('cqid'=>substr($rkey,4),'cqanswer'=>$posted[$rkey]);
3610  $cqid = substr($rkey,4);
3611  $mfalist[$cqid] = $posted[$rkey];
3612  }
3613  }
3614  return ($mfalist);
3615 }
3616 
3617 function MFQ_defined($posted) {
3618  # returns a count of MFA_* challenge question responses
3619  # in (sanitized) array of posted values
3620  $mfa = 0;
3621  foreach (array_keys($posted) as $rkey) {
3622  $m = strpos($rkey, 'MFA_');
3623  if ($m !== FALSE && $m == 0) {
3624  $mfa++;
3625  }
3626  }
3627  return ($mfa);
3628 }
3629 
3630 function ANS_list($arr) {
3631  $anslist = array();
3632  foreach (array_keys($arr) as $rkey) {
3633  $m = strpos($rkey, 'ANS_');
3634  if ($m !== FALSE && $m == 0) {
3635  $anslist[] = substr($rkey, 4);
3636  }
3637  }
3638  return ($anslist);
3639 }
3640 
3641 function Return_ResponseOK($CU, $MEMBER, $UID, $SENDKEY, $MESSAGE) {
3642 # was sending <LASTUPDATE>" . $HB_ENV['lastupdate'] . "</LASTUPDATE>
3643 # but it was always empty as HB_ENV is neither passed in nor global
3644 # so took it out
3645 # also a couple of the alert-related calls were using an 'OFXPOST' tag at the top
3646 # but I made them all consistent
3647 
3648  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO', 'MESSAGE'=> array('INFO' => $MESSAGE)),
3649  'DTSERVER' => date('YmdHis'),
3650  'MEMBER' => $UID );
3651 
3652  if (is_array($SENDKEY)) {
3653  foreach ($SENDKEY as $key => $value) {
3654  $reply_arr[$key] = $value;
3655  }
3656  }
3657 
3658  return $reply_arr;
3659 }
3660 
3661 function Return_AllowedUpdate($CU, $MEMBER, $UID, $HB_ENV) {
3662 
3663 // $apptoken = MakeUserkey($CU, $MEMBER, $UID);
3664  $apptoken = MakeSessionUserkey( $HB_ENV );
3665 
3666  $upd_grace = $HB_ENV['Ffremain'];
3667  $upd_wait = ($upd_grace == 0 ? "You must update your credentials now." :
3668  "You must update your credentials within the next $upd_grace login" . ($upd_grace == 1 ? '.' : 's.') );
3669 
3670  $reply_arr = array('STATUS' => array('CODE' =>3110,'SEVERITY' => 'INFO'),
3671  'SREQ' => $HB_ENV['forceupdate'],
3672  'UREQ' => $HB_ENV['requpdate'],
3673  'DTSERVER' => date('YmdHis'),
3674  'MEMBER' => $UID,
3675  'USERKEY' => $apptoken );
3676 
3677  $reply_arr['ALLOWUPD'] = array();
3678 
3679  if ($HB_ENV['forceupdate'] != 0) {
3680  $reply_arr['ALLOWUPD']['UPDCANWAIT'] = $upd_grace;
3681  $reply_arr['ALLOWUPD']['UPDWAITPHRASE']=$upd_wait;
3682  }
3683  $reply_arr['ALLOWUPD']['PASSWORD'] = array(
3684  'ALLOW' => (($HB_ENV['allowupdate'] & 1) ? "YES" : "NO"),
3685  'REQ' => (($HB_ENV['forceupdate'] & 1) ? "YES" : "NO"));
3686  $reply_arr['ALLOWUPD']['EMAIL'] = array(
3687  'ALLOW' => (($HB_ENV['allowupdate'] & 2) ? "YES" : "NO"),
3688  'REQ' => (($HB_ENV['forceupdate'] & 2) ? "YES" : "NO"));
3689  $reply_arr['ALLOWUPD']['CHALLENGE'] = array(
3690  'ALLOW' => (($HB_ENV['allowupdate'] & 4) ? "YES" : "NO"),
3691  'REQ' => (($HB_ENV['forceupdate'] & 4) ? "YES" : "NO"));
3692  $reply_arr['ALLOWUPD']['USERALIAS'] = array(
3693  'ALLOW' => (($HB_ENV['allowupdate'] & 8) ? "YES" : "NO"),
3694  'REQ' => (($HB_ENV['forceupdate'] & 8) ? "YES" : "NO"));
3695  $reply_arr['ALLOWUPD']['PHONE'] = array(
3696  'ALLOW' => (($HB_ENV['allowupdate'] & 16) ? "YES" : "NO"),
3697  'REQ' => (($HB_ENV['forceupdate'] & 16) ? "YES" : "NO"));
3698 
3699  return $reply_arr;
3700 }
3701 
3702 function Return_ReqUpdate($CU, $MEMBER, $UID, $HB_ENV, $SENDKEY) {
3703  $dbh = $HB_ENV['dbh'];
3704  $MC = $HB_ENV['MC'];
3705 
3706 // $apptoken = MakeUserkey($CU, $MEMBER, $UID);
3707  $apptoken = MakeSessionUserkey( $HB_ENV );
3708 
3709  # forceupdate 1 = reset password
3710  # forceupdate 4 = reset security w/ challenge questions
3711  # forceupdate 16 = reset security w/ access codes
3712  # send updRemember if reset pwd or reset security is set
3713  $upd_remember = $HB_ENV['forceupdate'] & 21;
3714  $upd_grace = $HB_ENV['Ffremain'];
3715  $upd_wait = ($upd_grace == 0 ? "You must update your credentials now." :
3716  "You must update your credentials within the next $upd_grace login" . ($upd_grace == 1 ? '.' : 's.') );
3717 
3718  $reply_arr = array('STATUS' => array('CODE' =>3100,'SEVERITY' => 'INFO'),
3719  'SREQ' => $HB_ENV['forceupdate'],
3720  'UREQ' => $HB_ENV['requpdate'],
3721  'DTSERVER' => date('YmdHis'),
3722  'MEMBER' => $HB_ENV['Uid'],
3723  'USERKEY' => $apptoken );
3724 
3725  if (is_array($SENDKEY)) {
3726  foreach ($SENDKEY as $key => $value) {
3727  $reply_arr[$key] = $value;
3728  }
3729  }
3730 
3731  $reply_arr['REQUIREUPD'] = array();
3732 
3733  if ($HB_ENV['forceupdate'] != 0 && $HB_ENV['requpdate'] == 0) {
3734  $reply_arr['REQUIREUPD']['UPDCANWAIT'] = $upd_grace;
3735  $reply_arr['REQUIREUPD']['UPDWAITPHRASE']=$upd_wait;
3736  }
3737  if ($upd_remember) {
3738  $reply_arr['REQUIREUPD']['UPDREMEMBER']='Remember This Device';
3739  }
3740  $upd_req = array();
3741 
3742  if ((($HB_ENV['forceupdate'] & 4) == 4 && $HB_ENV['requpdate'] == 0) || ($HB_ENV['requpdate'] & 4) == 4) {
3743  # 2-factor and either force reset or not enough questions selected yet
3744  # Security Reset: send master list of challenge questions
3745  $upd_req = array('UPDPHRASEID' => 'CHALLENGE',
3746  'UPDCONFLABEL' => 'This confidence word is used to identify and prevent phishing attempts when you access home banking through the web. It is not used in this app, but you are asked to set it now in case you later access your account through the web.',
3747  'UPDCONFIDENCE' => htmlentities($HB_ENV['confidence'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE),
3748  'UPDPHRASECOUNT' => $HB_ENV['cu_chgqst_count']);
3749  if ($HB_ENV['cu_chgqst_count'] > 0) {
3750  $upd_req['UPDPHRASELABEL'] = "Please select {$HB_ENV['cu_chgqst_count']} challenge questions";
3751  $upd_req['UPDCHOICELIST'] = array();
3752  $questlist = GetChallengeQuestions("DISPLAY", $dbh, $HB_ENV, $MC);
3753  foreach ($questlist as $QstValue) {
3754  $upd_req['UPDCHOICELIST'][]['CHOICEITEM'] = array('CQID' => $QstValue['cqid'],
3755  'CQTEXT' => htmlentities($QstValue['display'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
3756  }
3757  if (($HB_ENV['requpdate'] & 4) == 4) {
3758  # 'on-demand' update - send current selected questions/responses
3759  $upd_req['CURRSELECTED'] = array();
3760  foreach ($HB_ENV['MFA']['answers'] as $quest_id => $quest_resp) {
3761  $upd_req['CURRSELECTED'][]['SELECTEDITEM'] = array('CQID' => $quest_id,
3762  'CQRESP' => htmlentities($quest_resp, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
3763  }
3764  }
3765  }
3766  $reply_arr['REQUIREUPD'][]['REQUPD'] = $upd_req;
3767  }
3768 
3769  if ((($HB_ENV['forceupdate'] & 1) == 1 && $HB_ENV['requpdate'] == 0) || ($HB_ENV['requpdate'] & 1) == 1) {
3770  # Password
3771 
3772  $noticesAry = Get_NoticeInfo($dbh, $HB_ENV, $HB_ENV['MC'], "P", "pwdRules", false);
3773  $hasRules = false;
3774 
3775  if ( $noticesAry["status"]["code"] == "000" && $noticesAry["notice"][0]["notice_id"] ) {
3776  $hasRules = true;
3777  $helpdoc = $noticesAry["notice"][0]["notice_linktarget"];
3778  }
3779  $pwdRequires = Get_PwdRules($dbh, $HB_ENV);
3780  $upd_req = array('UPDPHRASEID' => 'PASSWORD',
3781  'UPDPHRASELABEL' => 'Please select a new password.',
3782  'PWDRULESLINK' => ($hasRules ? htmlentities($helpdoc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE) : ''),
3783  'PWDRULESLABEL' => ($hasRules ? 'I have read the Recommended Password Guidelines' : ''),
3784  'PWDADVLABEL' => ($hasRules ? 'Recommended Guidelines' : ''));
3785  foreach ($pwdRequires as $pwdkey => $pwdval) {
3786  $pwdkey = strtoupper($pwdkey);
3787  $upd_req['PWDREQUIRES'][$pwdkey] = $pwdval;
3788  }
3789 // if ($pwdRequires['spec'] > 0) {
3790  $pwdSpecChar = Get_PwdSpecialCharacters();
3791  $upd_req['PWDREQUIRES']['PWDSPECIALCHARS'] = htmlspecialchars($pwdSpecChar, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
3792 // }
3793 
3794  $reply_arr['REQUIREUPD'][]['REQUPD'] = $upd_req;
3795  }
3796  if ((($HB_ENV['forceupdate'] & 6) > 0 && $HB_ENV['requpdate'] == 0) || ($HB_ENV['requpdate'] & 2) == 2) {
3797  # verify email is set or email is empty,
3798  # or we are sending the CHALLENGE set so include email with it (apps treat email as extra challenge question)
3799  $upd_req = array('UPDPHRASEID' => 'EMAIL',
3800  'UPDPHRASELABEL' => 'Please provide your email address.',
3801  'CURRENTEMAIL' => htmlentities($HB_ENV['Ml'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE),
3802  'CURRENTOPTIN' => $HB_ENV['egenl_flag'],
3803  'OPTIN_PHRASE' => $MC->msg('Yes Email List'));
3804 
3805  $reply_arr['REQUIREUPD'][]['REQUPD'] = $upd_req;
3806  }
3807 
3808  if ((($HB_ENV['forceupdate'] & 8) == 8 && $HB_ENV['requpdate'] == 0) || ($HB_ENV['requpdate'] & 8) == 8) {
3809  # user_alias
3810  $maymust = (($HB_ENV['Fset2'] & $GLOBALS['CU2_ALIAS_REQ']) == $GLOBALS['CU2_ALIAS_REQ'] ? 'must' : 'may');
3811  $aliaslabel = $MC->combo_msg('Username Set', 0, '#MAYMUST#', "$maymust");
3812  $upd_req = array('UPDPHRASEID' => 'USERALIAS',
3813  'UPDPHRASEREQ' => ($HB_ENV['alias'] == 'NONE' ? 'NO' : $HB_ENV['alias']),
3814  'UPDPHRASELABEL' => htmlentities($aliaslabel, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
3815  if (($HB_ENV['requpdate'] & 8) == 8) {
3816  # 'on-demand' update - send current user alias
3817  $upd_req['CURRENTALIAS'] = htmlentities($HB_ENV['useralias'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
3818  }
3819  $reply_arr['REQUIREUPD'][]['REQUPD'] = $upd_req;
3820  }
3821  if ((($HB_ENV['forceupdate'] & 16) == 16 && $HB_ENV['requpdate'] == 0) || ($HB_ENV['requpdate'] & 16) == 16) {
3822 
3823  $upd_req = array('UPDPHRASEID' => 'PHONES',
3824  'UPDCONFLABEL' => 'This confidence word is used to identify and prevent phishing attempts when you access home banking through the web. It is not used in this app, but you are asked to set it now in case you later access your account through the web.',
3825  'UPDCONFIDENCE' => htmlentities($HB_ENV['confidence'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
3826  # add array entries for phone numbers
3827  $upd_req['MOBILE']['MAXLIMIT'] = MAX_PHONES;
3828  $upd_req['MOBILE']['UPDMBLLABEL'] = $MC->msg('TXT Enabled');
3829  $phones = GetUserPhones($CU, $UID, $HB_ENV);
3830  if (is_array($phones['mobile'])) {
3831  foreach ($phones['mobile'] as $ph => $phnum) {
3832 // * strip the punctuation
3833  $upd_req['MOBILE'][]['PHONE'] = preg_replace('/\D/', '', $phnum);
3834  }
3835  }
3836  $reply_arr['REQUIREUPD'][]['REQUPD'] = $upd_req;
3837  }
3838 
3839  return $reply_arr;
3840 } // end Return_ReqUpdate
3841 
3842 function MakeUserkey($CU, $MEMBER, $CAUTH) {
3843  global $apptokenkey;
3844  $appexpires = time() + 900; # 15 minutes
3845  $apphash = MD5($apptokenkey . MD5(join(':', array($apptokenkey, $appexpires, $CU, $MEMBER, $CAUTH))));
3846  # $mytoken="H=$hash&E=$expires&A=$cauth_member&C=$current_member";
3847 
3848  $apptoken = urlencode("H=$apphash&E=$appexpires&A=$MEMBER&C=$CAUTH");
3849  return $apptoken;
3850 }
3851 
3852 function CheckUserkey($CU, $USERKEY, $apptokenkey) {
3853 
3854  try {
3855  $result = array('Status' => array('Code' =>0, 'Message' => 'Success'));
3856 
3857  $apptokarr = array();
3858  parse_str(urldecode($USERKEY), $apptokarr);
3859 
3860  if ($apptokarr['E'] < time()) {
3861  throw new Exception("Invalid Credentials (Expired Token) " . __LINE__,15510);
3862  }
3863 
3864  if (is_null($apptokarr['E']) || is_null($apptokarr['A']) || is_null($apptokarr['H'])) {
3865  throw new Exception("Invalid Credentials (Partial Token) " . __LINE__,15510);
3866  }
3867  # if no C= value, assume oldstyle userkey
3868  # and set member as A= value so hash works
3869  # for odyssey A & C are swapped
3870  # A is Uid, C is primary account
3871 
3872  if (is_null($apptokarr['C'])) {
3873  $CAUTH = $apptokarr['A'];
3874  } else {
3875  $CAUTH = $apptokarr['C'];
3876  }
3877  $MEMBER = $apptokarr['A'];
3878  $EXPIRES = $apptokarr['E'];
3879  $hash = MD5($apptokenkey .
3880  MD5(join(':', array($apptokenkey, $EXPIRES, $CU, $MEMBER, $CAUTH))));
3881  if ($apptokarr['H'] != $hash) {
3882  throw new Exception("Invalid Credentials (Corrupted Token) " . __LINE__,15510);
3883  }
3884  } catch (Exception $e) {
3885  $result = array('Status' => array('Code' => $e->getCode(), 'Message' => 'Failed ' . $e->getMessage()));
3886  }
3887  return $result;
3888 } // end CheckUserkey
3889 
3890 /* Build a session token for subsequent validation. The session token is the same value
3891  * the desktop ticket cookie uses.
3892  */
3893 function MakeSessionUserkey( $pHBEnv ) {
3894 global $gSavedUserkey;
3895 
3896  $now = time();
3897  $newCtime = $now;
3898  $newCe = $now + $pHBEnv['SYSENV']['ticket']['expires'];
3899 
3900  $newSet = "Ctime={$newCtime}&Ce={$newCe}";
3901 
3902  return urlencode( BuildSessionTicketStr($pHBEnv, $gSavedUserkey, $newSet ) );
3903 } // end MakeSessionUserkey
3904 
3905 /* Check the given session token for validity. The session token has a lot of information
3906  * parts, so return that as an array. This is similar to Check_Member_Credentials for the
3907  * desktop.
3908  */
3909 function CheckSessionUserkey( $pHBEnv, $sessionStr ) {
3910  try {
3911  $result = array('Status' => array('Code' =>0, 'Message' => 'Success'));
3912 
3913  // See if need another decode because the RDC File Upload comes in encoded.
3914  // This finds the "Cu=" string that is encoded, which should always be somewhere in the cookie string.
3915  if ( strpos( $sessionStr, "Cu%3D" ) !== false ) {
3916  $sessionStr = urldecode( $sessionStr );
3917  }
3918 
3919  $returnArray = CheckSessionTicket( $pHBEnv, $sessionStr );
3920  if ( !is_array( $returnArray['result'] ) ) {
3921 
3922  // THE SECURITY VERIFICATION FAILED!
3923  throw new Exception( "Session Expired", 15510 );
3924  }
3925 
3926  $result["data"] = $returnArray["result"];
3927  } catch (Exception $e) {
3928  $result = array('Status' => array('Code' => $e->getCode(), 'Message' => 'Failed ' . $e->getMessage()));
3929  }
3930 
3931  return $result;
3932 } // end CheckSessionUserkey
3933 
3934 function CleanWordQuotes($instring) {
3935  /**
3936  # try to clean up the funky quote characters cut-n-paste from MS Word
3937  * orignal code had the following converions:
3938  * char codes entity codes
3939  * �? 212 & 145 to 8216 curly left single quote
3940  * �? 213 & 146 to 8217 apostrophe, curly right single quote
3941  * ⤽ 210 & 147 to 8220 curly left double quote
3942  * ⤝ 211 & 148 to 8221 curly right double quote
3943  * �? 209 & 151 to 8212 em dash
3944  * �? 208 & 150 to 8211 en dash
3945  * �? 201 & 133 to 8230 ellipsis
3946  *
3947  * replacing with character codes didn't seem to work
3948  * android showed the funky 'a' values above instead of expected char
3949  * maybe due to ISO-8859 vs UTF-8 issues?
3950  * replaced with simple ascii instead, and that appears to work
3951  */
3952  $search = array(
3953  chr(145),
3954  chr(146),
3955  chr(147),
3956  chr(148),
3957  chr(151),
3958  chr(150),
3959  chr(133),
3960  "\342\200\234",
3961  "\342\200\235"
3962  );
3963 
3964  $replace = array(
3965  '\'',
3966  '\'',
3967  '"',
3968  '"',
3969  '-',
3970  '-',
3971  '&#8230;',
3972  '"',
3973  '"'
3974  );
3975 
3976  $instring = str_replace($search, $replace, $instring);
3977  return $instring;
3978 }
3979 
3980 function Load_HB_ENV($dbh, $CU, $MEMBER, &$HB_ENV, $CFGFLAG=0) {
3981  // ** First thing is First Check the username
3982  $username = trim($MEMBER);
3983  $live = $HB_ENV['live'];
3984 
3985  # on first (method MFA) login, MEMBER will have username
3986  # after that, (method SSO) MEMBER will have USERID
3987 
3988 // if ($HB_ENV['AuthMode'] == 'MFQ') {
3989  # AuthMode=MFQ Multi-factor w/questions
3990  $qby = "cuuser.user_name ilike '" . prep_save($MEMBER) . "' ";
3991 // } else {
3992 // # AuthMode=SSO w/Userkey
3993 // $qby = "cuuser.user_id = $MEMBER ";
3994 // }
3995 
3996 
3997  // ** QUERY THE USER INFORMATION
3998  $sqluser = "SELECT cuuser.user_id as user_id, trim(cuuser.user_name) as user_name,
3999  trim(cuuser.passwd) as password, forcechange, forceremain, failedremain,
4000  pwchange, trim(email) as email, egenl_flag, trim(confidence) as confidence,
4001  cuuser.user_id as cuuser_id,
4002  cuuser.group_id as cuuser_group_id, priorlogin, failedlogin, msg_tx,
4003  userflags & {$GLOBALS['MEM_FORCE_RESET']}::int4 as mem_force_reset, userflags,
4004  histdays, gracelimit, trmemomaxlen, cuadmin.tz, mfaquest, primary_account
4005 
4006  FROM {$CU}user as cuuser
4007  JOIN cuadmin on cuadmin.cu = '" . prep_save($CU) . "'
4008  WHERE $qby ";
4009 
4010  $mbr_sth = db_query($sqluser, $dbh);
4011  if (db_num_rows($mbr_sth) == 0) {
4012  $insflag = 1;
4013  } else {
4014  $insflag = 0;
4015  $drow = db_fetch_array($mbr_sth, 0);
4016 
4017  }
4018 
4019  $drow = db_fetch_array($mbr_sth, 0);
4020  $HB_ENV['Cu'] = $CU;
4021  $HB_ENV['cu'] = $CU;
4022  $HB_ENV['chome'] = strtolower($CU);
4023  $HB_ENV['Uid'] = $drow['user_id'];
4024  $HB_ENV['Cn'] = $drow['user_name'];
4025  $HB_ENV['username'] = $drow['user_name'];
4026  $HB_ENV['user_name'] = $drow['user_name'];
4027  $HB_ENV['confidence'] = $drow['confidence'];
4028 // $HB_ENV['Ml'] = urlencode($drow['email']);
4029  $HB_ENV['Ml'] = $drow['email'];
4030  $HB_ENV['savemail'] = $drow['email'];
4031  $HB_ENV['egenl_flag'] = urlencode($drow['egenl_flag']);
4032  $HB_ENV['password'] = $drow['password'];
4033  $HB_ENV['userflags'] = $drow['userflags'];
4034  $HB_ENV['failedremain'] = $drow['failedremain'];
4035  $HB_ENV['Ffchg'] = $drow['forcechange'];
4036  $HB_ENV['Ffremain'] = $drow['forceremain'];
4037  $HB_ENV['dbforceremain'] = $drow['forceremain'];
4038  $HB_ENV['Ffreset'] = (is_null($drow['mem_force_reset']) ? 0 : $drow['mem_force_reset']);
4039 // $HB_ENV['Fmsg_tx'] = ($drow['msg_tx'] | $CFGFLAG); # this would add any CFGFLAG passed to msg_tx only
4040  $HB_ENV['Fmsg_tx'] = (is_null($drow['msg_tx']) ? 0 : $drow['msg_tx']);
4041  $HB_ENV['cfgflag'] = $CFGFLAG; # set cfgflag if CFGFLAG if passed
4042  $HB_ENV['Fverifyml'] = ($drow['msg_tx'] & 512);
4043  # mammoth data calls use Clw; odyssey switched to livewait so define both
4044  $HB_ENV['Clw'] = ((is_null($HB_ENV['livewait']) || $HB_ENV['livewait'] == 0) ? 300 : $HB_ENV['livewait']);
4045  $HB_ENV['lastupdate'] = (empty($drow['lastupdate']) ? "Unknown" : urlencode(trim($drow['lastupdate'])));
4046  $HB_ENV['pwchange'] = (is_null($drow['pwchange']) ? date('Ymd') : $drow['pwchange']);
4047 // $HB_ENV['employee'] = $drow['employee'];
4048  $HB_ENV['HCUPOST'] = array(); # set empty parameter array
4049  if ($HB_ENV['flagset2'] & $GLOBALS['CU2_ALIAS_REQ']) {
4050  $alias = 'REQUIRE';
4051  } else {
4052  $alias = 'ALLOW';
4053  }
4054 // } elseif ($HB_ENV['flagset2'] & $GLOBALS['CU2_ALIAS_OK']) {
4055 // $alias = 'ALLOW';
4056 // } else {
4057 // $alias = 'NONE';
4058 // }
4059  $HB_ENV['alias'] = $alias; # this shouldn't be needed - oh but it is
4060  # alias is always allowed
4061  # required means must start with non-digit
4062  $HB_ENV['Fset'] = $HB_ENV['flagset'];
4063  $HB_ENV['Fset2'] = $HB_ENV['flagset2'];
4064  $HB_ENV['Fset3'] = $HB_ENV['flagset3'];
4065 
4066  // * Create the MFA Quest Array (or set empty if Legacy, which shouldn't happen in Odyssey...)
4067  $HB_ENV['MFA'] = ($HB_ENV['cver'] == 'L' ? array() : HCU_MFADecode(HCU_JsonDecode($drow['mfaquest'])));
4068  $HB_ENV['mfaquest'] = $drow['mfaquest'];
4069  $HB_ENV['savecqid'] = $HB_ENV['MFA']['challenge'];
4070  $HB_ENV['chcount'] = $HB_ENV['MFA']['mfacount'];
4071 
4072  $FORCEUPDATE = 0;
4073  if ($HB_ENV['Ffchg'] == 'Y') {
4074  $FORCEUPDATE += 1; #password
4075  }
4076  if ($HB_ENV['Fverifyml'] == 512 || $HB_ENV['Ml'] == '') {
4077  $FORCEUPDATE += 2; # email
4078  }
4079 
4080  if ( intval($HB_ENV['flagset3'] & $GLOBALS['CU3_MFA_AUTHCODE']) == 0 &&
4081  ( $HB_ENV['Ffreset'] == 2 || $HB_ENV['chcount'] < $HB_ENV['cu_chgqst_count'] ) ) {
4082  $FORCEUPDATE += 4; #challenge questions
4083  }
4084 
4085  if (($HB_ENV['flagset2'] & $GLOBALS['CU2_ALIAS_REQ']) && !Check_Member_UseAlias($HB_ENV['user_name'])) {
4086  $FORCEUPDATE += 8;
4087  }
4088 
4089  if ( intval($HB_ENV['flagset3'] & $GLOBALS['CU3_MFA_AUTHCODE']) > 0 && $HB_ENV['Ffreset'] == 2 ) {
4090  $FORCEUPDATE += 16; #phone numbers
4091  }
4092 
4093  $HB_ENV['forceupdate'] = $FORCEUPDATE;
4094  $HB_ENV['allowupdate'] = 11; # password, email, and user_name update always allowed
4095  $HB_ENV['allowupdate'] += (intval($HB_ENV['flagset3'] & $GLOBALS['CU3_MFA_AUTHCODE']) == 0 ? 4 : 0); # can't update Challenge Questions if SAC in use
4096  $HB_ENV['allowupdate'] += (intval($HB_ENV['flagset3'] & $GLOBALS['CU3_MFA_AUTHCODE']) == 0 ? 0 : 16); # can only update contact phone if SAC in use
4097 #
4098  $HB_ENV['requpdate'] = 0; # assume at first this is not a 'getsettings' request
4099 
4100  if ( $HB_ENV['failedremain'] <= 0 ||
4101  ( ($HB_ENV['forceupdate'] & 29) > 0 && $HB_ENV['Ffremain'] <= 0 )
4102  ) {
4103 
4104  $HB_ENV['lockedacct'] = 1;
4105 } else {
4106  $HB_ENV['lockedacct'] = 0;
4107 }
4108 
4109 # eventually this will come from a new column in cuadmin
4110 # but for now....
4111  $HB_ENV['AppTimeout'] = intval($HB_ENV['SYSENV']['ticket']['expires'] * .8);
4112  $priorLogin = (trim(HCU_array_key_value('priorlogin',$drow))=='' ? 'None' : $drow['priorlogin']);
4113 
4114  $HB_ENV['Fplog'] = ($priorLogin == 'None' ? '' : (strftime("%D %R", mktime(
4115  substr($priorLogin, 11, 2), substr($priorLogin, 14, 2), substr($priorLogin, 17, 2), substr($priorLogin, 5, 2), substr($priorLogin, 8, 2), substr($priorLogin, 0, 4)))));
4116  $failedlogin = (trim(HCU_array_key_value('failedlogin',$drow))=='' ? 'None' : $drow['failedlogin']);
4117  $HB_ENV['Fflog'] = ($failedlogin == 'None' ? '' : (strftime("%D %R", mktime(
4118  substr($failedlogin, 11, 2), substr($failedlogin, 14, 2), substr($failedlogin, 17, 2), substr($failedlogin, 5, 2), substr($failedlogin, 8, 2), substr($failedlogin, 0, 4)))));
4119  // needed for building up URLs for webviews
4120  $HB_ENV["cuquery"] = http_build_query( array( "cu" => $CU ) );
4121 
4122  $tz = trim( HCU_array_key_value( "tz", $drow ) );
4123  $HB_ENV['tz'] = $tz == "" ? "US/Mountain" : ((strpos($tz,"/") === false ) ? "US/$tz" : $tz);
4124 }
4125 
4126 function fullFeed($dbh, $HB_ENV, $DTSTART, $DTEND, $BALONLY, $SENDAS, $KEYACCTID, $recent, $SENDKEY) {
4127 
4128 try {
4129 # gen OFX
4130  if (empty($DTSTART) || !sqlmdy($DTSTART)) {
4131  throw new Exception("Invalid Start Date $DTSTART", 2090);
4132  } else {
4133  $sqlstart = sqlmdy($DTSTART);
4134  }
4135 
4136  if (!empty($DTEND) && !sqlmdy($DTEND)) {
4137  throw new Exception('Invalid End Date', 2090);
4138  } else {
4139  $sqlend = $DTEND;
4140  }
4141  $sqlnone = date("Ymd", time() + (24 * 60 * 60)); # tomorrow
4142 
4143  $balances = Get_Balances($dbh, $HB_ENV);
4144 
4145  $expires = mktime(date("H"), date("i"), date("s"), date("m") + 3, date("d"), date("Y"));
4146 // $apptoken = MakeUserkey($HB_ENV['Cu'], $HB_ENV['username'], $HB_ENV['Uid']);
4147  $apptoken = MakeSessionUserkey( $HB_ENV );
4148  $cookiename = Return2FactorName($HB_ENV['Cu'], Get2FactorKeyString(), trim($HB_ENV['Cn']));
4149 
4150  $now = date('YmdHis');
4151 
4152  $postedDate = HCU_array_key_value("lastupdate", $HB_ENV);
4153  if ( $postedDate != "Unknown" && $postedDate != 1 && strlen( $postedDate ) > 0 ) {
4154  $cuDateTime = new DateTime( $postedDate );
4155  $cuDateTime->setTimezone(new DateTimeZone($HB_ENV["tz"]));
4156  $displayCUTime = $cuDateTime->format("m/d/Y g:ia T");
4157  } else {
4158  $displayCUTime = $postedDate;
4159  }
4160 
4161 
4162  // note: $HB_ENV["lastupdate"] is updated in Get_Balances()
4163  $reply_arr = array('STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'),
4164  'DTSERVER' => date('YmdHis'),
4165  'LASTUPDATE' => $displayCUTime,
4166  'LOGINPRIOR' => $HB_ENV["Fplog"],
4167  'LOGINFAIL' => $HB_ENV["Fflog"],
4168  'MEMBER' => $HB_ENV['Uid'],
4169  'SELECTED_MEMBER' => $HB_ENV['Uid'],
4170  'USERKEY' => $apptoken,
4171  'TIMEOUT' => $HB_ENV['AppTimeout']);
4172  if (HCU_array_key_value('stale',$HB_ENV) == 1) {
4173  $reply_arr['STALE'] = 'YES';
4174  }
4175 
4176  if (is_array($SENDKEY)) {
4177  foreach ($SENDKEY as $key => $value) {
4178  $reply_arr[$key] = $value;
4179  }
4180  }
4181 
4182  $featureaccts = array();
4183  foreach (array('ES','BP','RDC') as $SPEEDBUMP_FEATURE) {
4184  $featureaccts[$SPEEDBUMP_FEATURE] = array();
4185  $accountList = Get_FeatureAccounts( $HB_ENV, $SPEEDBUMP_FEATURE, "A" );
4186  foreach ($accountList as $acct) {
4187  // get the account and hash together
4188  $encryptedAccount = hcu_encrypturl($acct, $HB_ENV['historyHash']);
4189  $accountInfo = array( "MBRACCT" => $acct, "ACCOUNT" => array( "MBRACCT" => $acct, "ENCRYPTED" => $encryptedAccount ) );
4190 // $featureaccts[$SPEEDBUMP_FEATURE][]['MBRACCT'] = $acct;
4191  $featureaccts[$SPEEDBUMP_FEATURE][] = $accountInfo;
4192  }
4193  }
4194  $reply_arr['FEATUREACCTS'] = $featureaccts;
4195 
4196 // $mbracctlist = Get_UserAccounts($dbh, $HB_ENV['Cu'], $HB_ENV['Uid']);
4197 // $reply_arr['MBRACCTLIST'] = array();
4198 //
4199 // if(count($mbracctlist['data'])) {
4200 // foreach ($mbracctlist['data'] as $mbracct) {
4201 // $reply_arr['MBRACCTLIST'][]['MBRACCT'] = $mbracct;
4202 // }
4203 // }
4204 
4205  /*
4206  * all the conversion tools I have tried convert the raw xml into entity-encoded
4207  * so this is an ugly hack -- just put this distinctive string in,
4208  * and replace it with the actual menu text before sending the response
4209  *
4210  * this code, and the companion hack in send_response(), will be removed
4211  * once we can generate an app menu on the server side.
4212  */
4213 // if (HCU_array_key_exists('menu',$SENDKEY)) {
4214 // $reply_arr['menu'] = HCU_array_key_value('menu',$SENDKEY);
4215 // }
4216 
4217  if (count($balances['dp']) && (empty($KEYACCTID) || (!empty($KEYACCTID) && array_key_exists($KEYACCTID, $balances['dp'])))) {
4218 # for each $balances['dp'] Get_History & print
4219  foreach ($balances['dp'] as $balkey => $balinfo) {
4220  if ((!empty($KEYACCTID)) && $balkey != $KEYACCTID) {
4221  continue;
4222  }
4223  // filter out any not allowing view balances
4224  if ( !HCU_array_key_value('view_balances', $balinfo) ) {
4225  continue;
4226  }
4227 
4228  $itm_arr = array();
4229  $desc = htmlspecialchars($balinfo['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4230  $displaydesc = htmlspecialchars($balinfo['displaydesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4231  $atype = ($balinfo['certnumber'] == "0" ? $balinfo['accounttype'] : "{$balinfo['accounttype']}_{$balinfo['certnumber']}");
4232  $atype = htmlspecialchars(trim($atype), ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4233 
4234  switch ($balinfo['deposittype']) {
4235  case "Y":
4236  $acttype = "CHECKING";
4237  break;
4238  case "C": # Certificates
4239  $acttype = "INVESTMENT";
4240  break;
4241  case "I": # IRA accounts
4242  $acttype = "RETIREMENT";
4243  break;
4244  case "N":
4245  case "S":
4246  $acttype = "SAVINGS";
4247  break;
4248  default:
4249  $acttype = "";
4250  }
4251 
4252  $itm_arr['STMTTRNRS'] = array('TRNUID' => 0,
4253  'STATUS' => array('CODE' =>0,'SEVERITY' => 'INFO'));
4254 
4255  $itm_arr['STMTTRNRS']['STMTRS'] = array('CURDEF' => 'USD',
4256  'BANKACCTFROM' => array(
4257  'BANKID' => $HB_ENV['rt'],
4258  'ACCTID' => $balkey,
4259  'ACCTTYPE' => $acttype,
4260  'DESCRIPTION' => $desc));
4261  if ("$displaydesc" > '') {
4262  $itm_arr['STMTTRNRS']['STMTRS']['BANKACCTFROM']['DISPLAYDESC'] =$displaydesc;
4263  }
4264 
4265  if (!$BALONLY) {
4266 
4267  $histXML = dpHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent);
4268  if ($histXML['Status'] !== 'Success') {
4269  throw new Exception('DP History Error', 7601); # couldn't get dp history
4270  } else {
4271  $itm_arr['STMTTRNRS']['STMTRS']['BANKTRANLIST'] = $histXML['HISTarr'];
4272  }
4273  } else {
4274  $itm_arr['STMTTRNRS']['STMTRS']['BANKTRANLIST'] = array(
4275  'DTSTART' => "${sqlnone}000000",
4276  'DTEND' => "${sqlnone}000000");
4277  }
4278 
4279  $itm_arr['STMTTRNRS']['STMTRS']['LEDGERBAL'] = array(
4280  'BALAMT' => $balinfo['currentbal'],
4281  'DTASOF' =>$now);
4282  if (($HB_ENV['Fset'] & $GLOBALS['CU_SHOWAVAILABLE']) == $GLOBALS['CU_SHOWAVAILABLE']) {
4283  $itm_arr['STMTTRNRS']['STMTRS']['AVAILBAL'] = array(
4284  'BALAMT' => $balinfo['availablebal'],
4285  'DTASOF' =>$now);
4286  }
4287 
4288  $itm_arr['STMTTRNRS']['STMTRS']['EXTRAINFO'] = array(
4289  'DTASOF' => $now,
4290  'KEYACCTID' => $balkey,
4291  'MBRACCT' => $balinfo['accountnumber'],
4292  'YTDINTEREST' => $balinfo['ytdinterest'],
4293  'LYRINTEREST' => $balinfo['lastyrinterest']);
4294  if ($acttype == "CHECKING") {
4295  $itm_arr['STMTTRNRS']['STMTRS']['EXTRAINFO']['MICRACCOUNT'] = $balinfo['micraccount'];
4296  }
4297  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWHOLD'])== $GLOBALS['CU2_SHOWHOLD']) {
4298  $itm_arr['STMTTRNRS']['STMTRS']['EXTRAINFO']['HOLDTOTAL'] = $balinfo['holdtotal'];
4299  }
4300  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWPEND'])== $GLOBALS['CU2_SHOWPEND']) {
4301  $itm_arr['STMTTRNRS']['STMTRS']['EXTRAINFO']['PENDTOTAL'] = $balinfo['pendtotal'];
4302  }
4303  # parse through each key &, if not already loaded into result, push it in there
4304  foreach ($balinfo as $bkey => $bval) {
4305  switch ($bkey) {
4306  case 'description':
4307  case 'displaydesc':
4308  case 'certnumber':
4309  case 'deposittype':
4310  case 'currentbal':
4311  case 'availablebal':
4312  case 'accountnumber':
4313  case 'ytdinterest':
4314  case 'lastyrinterest':
4315  case 'micraccount':
4316  case 'holdtotal':
4317  case 'pendtotal':
4318  break;
4319  default:
4320  $upperkey = strtoupper($bkey);
4321  $itm_arr['STMTTRNRS']['STMTRS'][$upperkey] = htmlspecialchars($bval, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4322  break;
4323  }
4324  }
4325  $reply_arr['BANKMSGSRSV1'][] = $itm_arr;
4326 
4327  }
4328  }
4329 
4330 #
4331 # If CU2_SPEC18, try to get credit card loans
4332  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SPEC18']) == $GLOBALS['CU2_SPEC18']) {
4333 
4334  if (HCU_array_key_exists('cc',$balances) && count($balances['cc']) && (empty($KEYACCTID) || (!empty($KEYACCTID) && array_key_exists($KEYACCTID, $balances['cc'])))) {
4335 
4336  foreach ($balances['cc'] as $balkey => $balinfo) {
4337  if ((!empty($KEYACCTID)) && $balkey != $KEYACCTID) {
4338  continue;
4339  }
4340  // filter out any not allowing view balances
4341  if ( !HCU_array_key_value('view_balances', $balinfo) ) {
4342  continue;
4343  }
4344 
4345  $nextduedate = $balinfo['nextduedate'];
4346  $creditlimit = $balinfo['creditlimit'];
4347 
4348  $desc = $balinfo['description'];
4349  $desc = htmlspecialchars("$desc", ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4350  $displaydesc = $balinfo['displaydesc'];
4351  $displaydesc = htmlspecialchars("$displaydesc", ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4352  $loan = $balinfo['loan'];
4353  $balance = $balinfo['currentbal'];
4354  $creditlimit = $balinfo['creditlimit'];
4355  $available = $creditlimit - $balance;
4356  $available = ($available < 0 ? "" : $available);
4357  $cur_avail = (($HB_ENV['Fset2'] & $GLOBALS['CU2_CALL_CCAVAIL']) == $GLOBALS['CU2_CALL_CCAVAIL'] ?
4358  "Call" : $available);
4359 
4360 
4361  # open new loan set
4362  $itm_arr = array();
4363 
4364  $itm_arr['CCSTMTTRNRS']['CCSTMTRS'] = array('CURDEF' => 'USD',
4365  'CCACCTFROM' => array(
4366  'ACCTID' => $balkey,
4367  'DESCRIPTION' => $desc));
4368  if ("$displaydesc" > '') {
4369  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['CCACCTFROM']['DISPLAYDESC'] =$displaydesc;
4370  }
4371  if (trim($balinfo['hisinfo']) > '' && strtolower(trim($balinfo['hisinfo'])) != 'homecu') {
4372  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['CCACCTFROM']['HISTORYURL'] = urlencode($balinfo['hisinfo']);
4373  }
4374 
4375  if (!$BALONLY) {
4376  $histXML = ccHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent);
4377  if ($histXML['Status'] !== 'Success') {
4378  throw new Exception('CC History Error', 7602); # couldn't get cc history
4379  } else {
4380  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['BANKTRANLIST'] = $histXML['HISTarr'];
4381  }
4382  } else {
4383  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['BANKTRANLIST'] = array(
4384  'DTSTART' => "${sqlnone}000000",
4385  'DTEND' => "${sqlnone}000000");
4386  }
4387 
4388  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['LEDGERBAL'] = array(
4389  'BALAMT' => $balance,
4390  'DTASOF' =>$now);
4391  if ($cur_avail > 0) {
4392  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['AVAILBAL'] = array(
4393  'BALAMT' => $cur_avail,
4394  'DTASOF' =>$now);
4395  }
4396  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO'] = array(
4397  'DTASOF' => $now,
4398  'KEYACCTID' => $balkey,
4399  'MBRACCT' => $balinfo['accountnumber']);
4400 
4401  if ($HB_ENV['live'] && ($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWCCSB']) == $GLOBALS['CU2_SHOWCCSB'] && ($HB_ENV['Fset2'] & $GLOBALS['CU2_CC18NOINFO']) != $GLOBALS['CU2_CC18NOINFO']) {
4402  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['STMNTBAL'] = HCU_array_key_value('payoff',$balinfo);
4403  }
4404  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['PAYAMOUNT'] = $balinfo['paymentamount'];
4405 
4406  if (($HB_ENV['Fset'] & $GLOBALS['CU_HIDELOANDATE']) != $GLOBALS['CU_HIDELOANDATE']) {
4407  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['NEXTDUE'] = $nextduedate;
4408  }
4409  if (HCU_array_key_exists("interestrate", $balinfo)) {
4410  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['INTERESTRATE'] = $balinfo['interestrate'];
4411  }
4412  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['CREDITLIMIT'] = $creditlimit;
4413  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['YTDINTEREST'] = $balinfo['ytdinterest'];
4414  $itm_arr['CCSTMTTRNRS']['CCSTMTRS']['EXTRAINFO']['LYRINTEREST'] = $balinfo['lastyrinterest'];
4415 
4416  # parse through each key &, if not already loaded into result, push it in there
4417  foreach ($balinfo as $bkey => $bval) {
4418  switch ($bkey) {
4419  case 'nextduedate':
4420  case 'creditlimit':
4421  case 'description':
4422  case 'displaydesc':
4423  case 'loan':
4424  case 'currentbal':
4425  case 'creditlimit':
4426  case 'hisinfo':
4427  case 'accountnumber':
4428  case 'payoff':
4429  case 'paymentamount':
4430  case 'interestrate':
4431  case 'ytdinterest':
4432  case 'lastyrinterest':
4433  break;
4434  default:
4435  $upperkey = strtoupper($bkey);
4436  $itm_arr['CCSTMTTRNRS']['CCSTMTRS'][$upperkey] = htmlspecialchars($bval, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4437  break;
4438  }
4439  }
4440 
4441  $reply_arr['CREDITCARDMSGSRSV1'][] = $itm_arr;
4442 
4443  }
4444  }
4445  }
4446 
4447 # try to get loans
4448  if ((HCU_array_item_count("ln", $balances) > 0) && (empty($KEYACCTID) || (!empty($KEYACCTID) && array_key_exists($KEYACCTID, $balances['ln'])))) {
4449 
4450  foreach ($balances['ln'] as $balkey => $balinfo) {
4451  if ((!empty($KEYACCTID)) && $balkey != $KEYACCTID) {
4452  continue;
4453  }
4454  // filter out any not allowing view balances
4455  if ( !HCU_array_key_value('view_balances', $balinfo) ) {
4456  continue;
4457  }
4458 
4459  $itm_arr = array();
4460 
4461  $balance = $balinfo['currentbal'];
4462  $loan = $balinfo['loan'];
4463  $desc = htmlspecialchars($balinfo['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4464  $displaydesc = htmlspecialchars($balinfo['displaydesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4465  $payoff = HCU_array_key_value('payoff',$balinfo);
4466  $nextduedate = $balinfo['nextduedate'];
4467  $creditlimit = $balinfo['creditlimit'];
4468 
4469  # open new loan set
4470  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS'] = array('CURDEF' => 'USD',
4471  'LOANACCTFROM' => array(
4472  'LOANACCTID' => $balkey,
4473  'LOANACCTTYPE' => 'CONSUMER',
4474  'DESCRIPTION' => $desc));
4475  if ("$displaydesc" > '') {
4476  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['LOANACCTFROM']['DISPLAYDESC'] =$displaydesc;
4477  }
4478  if (trim($balinfo['hisinfo']) > '' && strtolower(trim($balinfo['hisinfo'])) != 'homecu') {
4479  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['LOANACCTFROM']['HISTORYURL'] = urlencode($balinfo['hisinfo']);
4480  }
4481 
4482  if (!$BALONLY) {
4483  $histXML = lnHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent);
4484  if ($histXML['Status'] !== 'Success') {
4485  throw new Exception('LN History Error', 7603); # couldn't get ln history
4486  } else {
4487  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['LOANTRANLIST'] = $histXML['HISTarr'];
4488  }
4489  } else {
4490  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['LOANTRANLIST'] = array(
4491  'DTSTART' => "${sqlnone}000000",
4492  'DTEND' => "${sqlnone}000000");
4493  }
4494  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['PRINBAL'] = array(
4495  'BALAMT' => $balance,
4496  'DTASOF' =>$now);
4497 
4498  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO'] = array(
4499  'DTASOF' => $now,
4500  'KEYACCTID' => $balkey,
4501  'MBRACCT' => $balinfo['accountnumber'],
4502  'PAYOFF' => $payoff,
4503  'PAYAMOUNT' => $balinfo['paymentamount']);
4504 
4505  if (($HB_ENV['Fset'] & $GLOBALS['CU_HIDELOANDATE']) != $GLOBALS['CU_HIDELOANDATE']) {
4506  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO']['NEXTDUE'] = $nextduedate;
4507  }
4508 
4509  if (HCU_array_key_exists("interestrate", $balinfo)) {
4510  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO']['INTERESTRATE'] = $balinfo['interestrate'];
4511  }
4512  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO']['CREDITLIMIT'] = $creditlimit;
4513  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO']['YTDINTEREST'] = $balinfo['ytdinterest'];
4514  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS']['EXTRAINFO']['LYRINTEREST'] = $balinfo['lastyrinterest'];
4515 
4516  # parse through each key &, if not already loaded into result, push it in there
4517  foreach ($balinfo as $bkey => $bval) {
4518  switch ($bkey) {
4519  case 'nextduedate':
4520  case 'creditlimit':
4521  case 'description':
4522  case 'displaydesc':
4523  case 'loan':
4524  case 'currentbal':
4525  case 'creditlimit':
4526  case 'hisinfo':
4527  case 'accountnumber':
4528  case 'payoff':
4529  case 'paymentamount':
4530  case 'interestrate':
4531  case 'ytdinterest':
4532  case 'lastyrinterest':
4533  break;
4534  default:
4535  $upperkey = strtoupper($bkey);
4536  $itm_arr['LOANSTMTTRNRS']['LOANSTMTRS'][$upperkey] = htmlspecialchars($bval, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4537  break;
4538  }
4539  }
4540 
4541  $reply_arr['LOANMSGSRSV1'][] = $itm_arr;
4542 
4543  }
4544  }
4545  } catch (Exception $e) {
4546  $code = $e->getCode();
4547  $message = $e->getMessage();
4548 
4549  $reply_arr = array('STATUS' => array('CODE' => $code, 'SEVERITY' => 'ERROR'),
4550  'MESSAGE'=> array('ERR' => htmlspecialchars($message, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE), 'ERRNO' => 999, 'ERRSTAT' =>'FAIL'));
4551 }
4552 
4553 // send_response($reply_arr,$SENDAS);
4554 
4555  return $reply_arr;
4556 } // end fullFeed
4557 
4558 function dpHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent) {
4559  try {
4560  $xmlResp = '';
4561  $reply_arr = array();
4562  $history = Get_History($dbh, $HB_ENV, $balkey, $sqlstart, $sqlend);
4563 # opening tags for transaction list
4564  $reply_arr['DTSTART'] ="${sqlstart}000000";
4565  $reply_arr['DTEND'] ="${sqlend}235959";
4566 
4567  if ( HCU_array_key_exists( $balkey , $history)) {
4568  foreach ($history[$balkey] as $tnum => $detl) {
4569 # process the list
4570  $itm_arr = array();
4571 
4572  $tranamount = $detl['amount'];
4573  $tranamount = str_replace(",", "", str_replace("$", "", $tranamount));
4574  $tranamount = sprintf("%.2f", $tranamount);
4575  $trbal = $detl['balance'];
4576  $trbal = str_replace(",", "", str_replace("$", "", $trbal));
4577  $trbal = sprintf("%.2f", $trbal);
4578  $check = (HCU_array_key_exists( 'checkno' , $detl) ? $detl['checkno'] : 0);
4579  $trandesc = $detl['description'];
4580  if ($trandesc < " " && $check != 0) {
4581  $trandesc = "CHK";
4582  }
4583  $trandesc = (preg_replace("/<BR>/", " ", $trandesc));
4584  $trandesc = (preg_replace("/&nbsp;/", " ", $trandesc));
4585  $longdesc = htmlspecialchars($trandesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4586  $shortdesc = substr(htmlspecialchars($trandesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE), 0, 31);
4587  $shortdesc = preg_replace('/&[^;]*$/', '', $shortdesc);
4588  $shortdesc = (trim($shortdesc) == '' ? '.' : $shortdesc);
4589 
4590  if ($tranamount < 0) {
4591  if ($balinfo['deposittype'] == 'Y' and $check != 0) {
4592  $trntype = 'CHECK';
4593  } else {
4594  $trntype = 'DEBIT';
4595  }
4596  } else {
4597  $trntype = 'CREDIT';
4598  }
4599  $itm_arr['TRNTYPE'] = $trntype;
4600  $itm_arr['DTPOSTED'] = $detl['date'];
4601 # DTUSER not needed for 211?
4602  $itm_arr['DTUSER'] = $detl['date'];
4603  $itm_arr['TRNAMT'] = $tranamount;
4604  $itm_arr['RUNBAL'] = $trbal;
4605  $itm_arr['FITID'] = $detl['traceno'];
4606  if ($balinfo['deposittype'] == 'Y' and $check != 0) {
4607  $itm_arr['CHECKNUM'] = $check;
4608  if (!empty($detl['ckitem']) && !empty($detl['ckhash'])) {
4609  $itm_arr['CKITEM'] = $detl['ckitem'];
4610  $itm_arr['CKHASH'] = $detl['ckhash'];
4611  }
4612  }
4613  $itm_arr['NAME'] = $shortdesc;
4614  if (strlen($longdesc) > 0) {
4615  $itm_arr['MEMO'] = $longdesc;
4616  }
4617 
4618  // $recent is in yyymmdd format
4619  $tranDate = preg_replace("/\D/", "", $detl['date'] );
4620  if ($tranDate < $recent) {
4621  $reply_arr[]['STMTTRN']=$itm_arr;
4622  } else {
4623  $reply_arr[]['RECENTTRN']=$itm_arr;
4624  }
4625  }
4626  }
4627 # closing tags for transaction list
4628 
4629  if ($HB_ENV['live'] == 0) {
4630  $pending = Get_ReqDetails($dbh, $HB_ENV, $balkey);
4631 # Get_ReqDetails returns txdesc already UTF-encoded. Problem?
4632  if ( HCU_array_key_exists( 'acctlist' , $pending) && HCU_array_key_exists( $balkey , $pending['acctlist']) ) {
4633  foreach ($pending['acctlist'][$balkey] as $tnum => $detl) {
4634 # process the list
4635  $itm_arr = array('TRACENO' => $detl['id'],
4636  'DTREQUEST' => $detl['postdate'],
4637  'TRNAMT' => $detl['amount'],
4638  'TRNDESC' => htmlspecialchars($detl['txdesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4639 
4640  $reply_arr['TXNPENDING'][]['REQUEST']= $itm_arr;
4641  }
4642  }
4643  }
4644 
4645  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWPEND']) == $GLOBALS['CU2_SHOWPEND']) {
4646  $pending = Get_PendDetails($dbh, $HB_ENV, $balkey);
4647  if ( HCU_array_key_exists( $balkey , $pending)) {
4648  foreach ($pending[$balkey] as $tnum => $detl) {
4649 # process the list
4650  $itm_arr = array('TRACENO' => $detl['traceno'],
4651  'DTREQUEST' => $detl['postdate'],
4652  'TRNAMT' => $detl['amount'],
4653  'TRNDESC' => htmlspecialchars($detl['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4654 
4655  $reply_arr['TXNPENDING'][]['ACHWAREHOUSE'] = $itm_arr;
4656  }
4657  }
4658  }
4659  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWHOLD']) == $GLOBALS['CU2_SHOWHOLD']) {
4660  $pending = Get_HoldDetails($dbh, $HB_ENV, $balkey);
4661  if ( HCU_array_key_exists( $balkey , $pending)) {
4662  foreach ($pending[$balkey] as $tnum => $detl) {
4663 # process the list
4664  $itm_arr = array('TRACENO' => $detl['traceno'],
4665  'DTREQUEST' => $detl['postdate'],
4666  'TRNAMT' => $detl['amount'],
4667  'TRNDESC' => htmlspecialchars($detl['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4668 
4669  $reply_arr['TXNPENDING'][]['PREAUTH'] = $itm_arr;
4670  }
4671  }
4672  }
4673  if (!HCU_array_key_exists('TXNPENDING',$reply_arr)) {
4674  $reply_arr['TXNPENDING']=array();
4675  }
4676 
4677  $result = array('Status' => 'Success', 'HISTarr' => $reply_arr, 'XMLstr' => assocArrayToXML($reply_arr, 'BANKTRANLIST', 0));
4678  } catch (Exception $e) {
4679  $result = array('Status' => 'Failed ' . $e->getMessage(), 'HISTarr' => array(), 'XMLstr' => '');
4680  }
4681  return $result;
4682 }
4683 
4684 function ccHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent) {
4685  try {
4686  $reply_arr = array();
4687 
4688  $xmlResp = '';
4689  $incchist = (trim(strtoupper($balinfo['hisinfo'])) == 'HOMECU' ? 1 : 0);
4690 
4691  if ($incchist) {
4692  $history = Get_History($dbh, $HB_ENV, $balkey, $sqlstart, $sqlend);
4693  $reply_arr['DTSTART'] ="${sqlstart}000000";
4694  $reply_arr['DTEND'] ="${sqlend}235959";
4695 
4696  if (count($history[$balkey])) {
4697  foreach ($history[$balkey] as $tnum => $detl) {
4698 
4699  $itm_arr = array();
4700 
4701  $principle = $detl['principal'];
4702  $interest = $detl['interest'];
4703 
4704  if ($principle < 0) {
4705  $trntype = "DEBIT";
4706  } else {
4707  $trntype = "CREDIT";
4708  }
4709 
4710  $totalpay = $detl['totalpay'];
4711  $trdesc = $detl['description'];
4712  $date = $detl['date'];
4713  $traceno = $detl['traceno'];
4714  $longdesc = htmlentities($trdesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4715  $shortdesc = substr(htmlentities($trdesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE), 0, 31);
4716  $shortdesc = preg_replace('/&[^;]*$/', '', $shortdesc);
4717  $shortdesc = (trim($shortdesc) == '' ? '.' : $shortdesc);
4718 
4719 # transaction data row
4720  $itm_arr['TRNTYPE'] = $trntype;
4721  $itm_arr['DTPOSTED'] = $detl['date'];
4722  $itm_arr['TRNAMT'] = $totalpay;
4723  if (($HB_ENV['Fset'] & $GLOBALS['CU_LNBALUNUSABLE']) != $GLOBALS['CU_LNBALUNUSABLE']) {
4724  $itm_arr['RUNBAL'] = $detl['balance'];
4725  }
4726  $itm_arr['FITID'] = $traceno;
4727  if (($HB_ENV['Fset'] & $GLOBALS['CU_SHOWLNTXNDESC']) == $GLOBALS['CU_SHOWLNTXNDESC']) {
4728  $$itm_arr['NAME'] = $shortdesc;
4729  $itm_arr['MEMO'] = $longdesc;
4730  }
4731 
4732  // $recent is in yyymmdd format
4733  $tranDate = preg_replace("/\D/", "", $detl['date'] );
4734  if ($tranDate < $recent) {
4735  $reply_arr[]['STMTTRN']=$itm_arr;
4736  } else {
4737  $reply_arr[]['RECENTTRN']=$itm_arr;
4738  }
4739  }
4740  }
4741 
4742  if ($HB_ENV['live'] == 0) {
4743  $pending = Get_ReqDetails($dbh, $HB_ENV, $balkey);
4744 # Get_ReqDetails returns txdesc already UTF-encoded. Problem?
4745  if (HCU_array_key_exists('acctlist', $pending) && HCU_array_key_exists($balkey, $pending['acctlist'])) {
4746  foreach ($pending['acctlist'][$balkey] as $tnum => $detl) {
4747 # process the list
4748  $itm_arr = array('TRACENO' => $detl['id'],
4749  'DTREQUEST' => $detl['postdate'],
4750  'TRNAMT' => $detl['amount'],
4751  'TRNDESC' => htmlspecialchars($detl['txdesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4752 
4753  $reply_arr['TXNPENDING'][]['REQUEST']= $itm_arr;
4754  }
4755  }
4756 
4757 #
4758  }
4759  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWPEND']) == $GLOBALS['CU2_SHOWPEND']) {
4760  $pending = Get_PendDetails($dbh, $HB_ENV, $balkey);
4761  if (count($pending[$balkey])) {
4762  foreach ($pending[$balkey] as $tnum => $detl) {
4763 # process the list
4764  $itm_arr = array('TRACENO' => $detl['traceno'],
4765  'DTREQUEST' => $detl['postdate'],
4766  'TRNAMT' => $detl['amount'],
4767  'TRNDESC' => htmlspecialchars($detl['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4768 
4769  $reply_arr['TXNPENDING'][]['ACHWAREHOUSE'] = $itm_arr;
4770 
4771  }
4772  }
4773  }
4774  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWHOLD']) == $GLOBALS['CU2_SHOWHOLD']) {
4775  $pending = Get_HoldDetails($dbh, $HB_ENV, $balkey);
4776  if (count($pending[$balkey])) {
4777  foreach ($pending[$balkey] as $tnum => $detl) {
4778 # process the list
4779  $itm_arr = array('TRACENO' => $detl['traceno'],
4780  'DTREQUEST' => $detl['postdate'],
4781  'TRNAMT' => $detl['amount'],
4782  'TRNDESC' => htmlspecialchars($detl['description'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4783 
4784  $reply_arr['TXNPENDING'][]['PREAUTH'] = $itm_arr;
4785 
4786  }
4787  }
4788  }
4789  if (!HCU_array_key_exists('TXNPENDING',$reply_arr)) {
4790  $reply_arr['TXNPENDING']=array();
4791  }
4792 
4793  }
4794  $result = array('Status' => 'Success', 'HISTarr' => $reply_arr, 'XMLstr' => assocArrayToXML($reply_arr, 'CREDITCARDTRANLIST', 0));
4795  } catch (Exception $e) {
4796  $result = array('Status' => 'Failed ' . $e->getMessage(), 'HISTarr' => array(), 'XMLstr' => '');
4797  }
4798  return $result;
4799 }
4800 
4801 function lnHistXML($dbh, $HB_ENV, $balkey, $balinfo, $sqlstart, $sqlend, $recent) {
4802  try {
4803  $reply_arr = array();
4804 
4805  $xmlResp = '';
4806  $history = Get_History($dbh, $HB_ENV, $balkey, $sqlstart, $sqlend);
4807 
4808  $reply_arr['DTSTART'] = "${sqlstart}000000";
4809  $reply_arr['DTEND'] = "${sqlend}235959";
4810 
4811  if (HCU_array_key_exists($balkey, $history)) {
4812  foreach ($history[$balkey] as $tnum => $detl) {
4813  $itm_arr = array();
4814 
4815  $principle = $detl['principal'];
4816  $interest = $detl['interest'];
4817  $totalpay = $detl['totalpay'];
4818  $traceno = $detl['traceno'];
4819  $trdesc = $detl['description'];
4820 
4821  if ($principle < 0) {
4822  $trntype = "PAYMENT";
4823  } else {
4824  $trntype = "ADVANCE";
4825  }
4826 
4827  $longdesc = htmlentities($trdesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE);
4828  $shortdesc = substr(htmlentities($trdesc, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE), 0, 31);
4829  $shortdesc = preg_replace('/&[^;]*$/', '', $shortdesc);
4830  $shortdesc = (trim($shortdesc) == '' ? '.' : $shortdesc);
4831 
4832 # transaction data row
4833 
4834  $itm_arr['LOANTRNTYPE'] = $trntype;
4835  $itm_arr['DTPOSTED'] = $detl['date'];
4836  if (($HB_ENV['Fset'] & $GLOBALS['CU_SHOWLNTXNSPLIT']) == $GLOBALS['CU_SHOWLNTXNSPLIT']) {
4837  $itm_arr['TRNAMT'] = $totalpay;
4838  $itm_arr['PRINAMT'] = $principle;
4839  $itm_arr['INTAMT'] = $interest;
4840  } else {
4841  $itm_arr['TRNAMT'] = $principle;
4842  }
4843  if (($HB_ENV['Fset'] & $GLOBALS['CU_LNBALUNUSABLE']) != $GLOBALS['CU_LNBALUNUSABLE']) {
4844  $itm_arr['RUNBAL'] = $detl['balance'];
4845  }
4846  $itm_arr['FITID'] = $traceno;
4847  if (($HB_ENV['Fset'] & $GLOBALS['CU_SHOWLNTXNDESC']) == $GLOBALS['CU_SHOWLNTXNDESC']) {
4848  $itm_arr['NAME'] = $shortdesc;
4849  $itm_arr['MEMO'] = $longdesc;
4850  }
4851 
4852  // $recent is in yyymmdd format
4853  $tranDate = preg_replace("/\D/", "", $detl['date'] );
4854  if ($tranDate < $recent) {
4855  $reply_arr[]['LOANSTMTTRN']=$itm_arr;
4856  } else {
4857  $reply_arr[]['RECENTTRN']=$itm_arr;
4858  }
4859  }
4860  }
4861 
4862  if ($HB_ENV['live'] == 0) {
4863  $pending = Get_ReqDetails($dbh, $HB_ENV, $balkey);
4864 
4865 # Get_ReqDetails returns txdesc already UTF-encoded. Problem?
4866  if (HCU_array_key_exists('acctlist', $pending) && HCU_array_key_exists($balkey, $pending['acctlist'])) {
4867  foreach ($pending['acctlist'][$balkey] as $tnum => $detl) {
4868 # process the list
4869  $itm_arr = array('TRACENO' => $detl['id'],
4870  'DTREQUEST' => $detl['postdate'],
4871  'TRNAMT' => $detl['amount'],
4872  'TRNDESC' => htmlspecialchars($detl['txdesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4873 
4874  $reply_arr['TXNPENDING'][]['REQUEST'] = $itm_arr;
4875  }
4876  }
4877  }
4878 
4879  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWPEND']) == $GLOBALS['CU2_SHOWPEND']) {
4880  $pending = Get_PendDetails($dbh, $HB_ENV, $balkey);
4881  if (HCU_array_key_exists($balkey, $pending)) {
4882  foreach ($pending[$balkey] as $tnum => $detl) {
4883  $itm_arr = array('TRACENO' => $detl['traceno'],
4884  'DTREQUEST' => $detl['postdate'],
4885  'TRNAMT' => $detl['amount'],
4886  'TRNDESC' => htmlspecialchars($detl['txdesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4887 
4888  $reply_arr['TXNPENDING'][]['ACHWAREHOUSE'] = $itm_arr;
4889 
4890  }
4891  }
4892  }
4893  if (($HB_ENV['Fset2'] & $GLOBALS['CU2_SHOWHOLD']) == $GLOBALS['CU2_SHOWHOLD']) {
4894  $pending = Get_HoldDetails($dbh, $HB_ENV, $balkey);
4895  if (HCU_array_key_exists($balkey, $pending)) {
4896  foreach ($pending[$balkey] as $tnum => $detl) {
4897  $itm_arr = array('TRACENO' => $detl['traceno'],
4898  'DTREQUEST' => $detl['postdate'],
4899  'TRNAMT' => $detl['amount'],
4900  'TRNDESC' => htmlspecialchars($detl['txdesc'], ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE));
4901 
4902  $reply_arr[]['PREAUTH']['REQUEST'] = $itm_arr;
4903  }
4904  }
4905  }
4906 
4907  if (!HCU_array_key_exists('TXNPENDING',$reply_arr)) {
4908  $reply_arr['TXNPENDING']=array();
4909  }
4910 
4911 
4912  $result = array('Status' => 'Success', 'HISTarr' => $reply_arr, 'XMLstr' => assocArrayToXML($reply_arr, 'LOANTRANLIST', 0));
4913  } catch (Exception $e) {
4914  $result = array('Status' => 'Failed ' . $e->getMessage(), 'HISTarr' => array(), 'XMLstr' => '');
4915  }
4916  return $result;
4917 }
4918 
4919 /**
4920  * Change an associative array to an XML string.
4921  *
4922  * @param array $ar associative array of items to be transformed to XML
4923  * @param string $base root xml tag to use
4924  *
4925  * @return string XML result string
4926  */
4927 function assocArrayToXML($ar, $base='APPFEED')
4928 {
4929  $xml = new SimpleXMLElement("<$base></$base>");
4930 
4931  $f = create_function('$f,$c,$a','
4932  foreach($a as $k=>$v) {
4933  if(is_array($v)) {
4934  if (is_numeric($k)) {
4935  $f($f,$c,$v);
4936  } else {
4937  $ch=$c->addChild($k);
4938  $f($f,$ch,$v);
4939  }
4940  } else {
4941  $c->addChild($k,htmlentities($v, ENT_NOQUOTES | ENT_XML1, "UTF-8", FALSE));
4942  }
4943  }');
4944  $f($f,$xml,$ar);
4945 
4946  $return = $xml->asXML();
4947 
4948  $return = str_replace('<?xml version="1.0"?>','',$return);
4949 
4950  return $return;
4951 }
4952 
4953 /*
4954  * This function calls FetchMenuArray which will return the list of menu
4955  * items that the user has rights to access. It then converts the returned
4956  * array information into the menu structure the apps expect. A later function
4957  * will convert it to xml.
4958  */
4959 function BuildUserMenu($dbh, $pHBEnv, $Platform='A') {
4960 
4961  $menuArray = FetchMenuArray( $pHBEnv, "A" );
4962 
4963  // need to conver the menu array into XML
4964  $userMenu = _BuildMenuStructure( $pHBEnv, $menuArray );
4965 
4966  return $userMenu;
4967 } // end BuildUserMenu
4968 
4969 /*
4970  * This function calls FetchMenuArray which will return the list of menu
4971  * items that the user has rights to access. It then converts the returned
4972  * array information into the menu structure the apps expect. A later function
4973  * will convert it to xml.
4974  * NOTE: This function returns the information so the menu can look like the
4975  * desktop menu.
4976  */
4977 function BuildFullUserMenu($dbh, $pHBEnv, $Platform='A') {
4978 
4979  $menuArray = FetchMenuArray( $pHBEnv, "A" );
4980 
4981  // return the full menu
4982  $userMenu = _BuildFullMenuStructure( $pHBEnv, $menuArray );
4983 
4984  return $userMenu;
4985 } // end BuildFullUserMenu
4986 
4987 // Build the menu structure the apps will use. The menu items need to
4988 // be in display order so the resultant XML is also in order.
4989 function _BuildMenuStructure( $pHBEnv, $pMenuArray ) {
4990  $returnMenuStructure = array();
4991 
4992  // Headers elements start a tag and then call routine to build children.
4993  // Stand-alone elements build a tag.
4994  for ( $i = 0; $i < count ( $pMenuArray ); $i++ ) {
4995  $menuElement = $pMenuArray[$i];
4996 
4997  $thisEntry = array();
4998  if ( $menuElement["menu_item_type"] == "H" ) {
4999  $groupDisplayName = HCU_array_key_value( "menu_display_name_en_US", $menuElement );
5000  $groupDisplayOrder = HCU_array_key_value( "menu_display_order", $menuElement );
5001 
5002  // get the menu id to find the children
5003  $menuItemId = HCU_array_key_value( 'menu_item_id', $menuElement );
5004 
5005  $childMenuItems = _BuildChildMenu( $pHBEnv, $menuElement["menu_children"], $menuItemId );
5006 
5007  // build the "child" features
5008  $childList = array();
5009  for ( $c = 0; $c < count( $childMenuItems ); $c++ ) {
5010  $childList[]= array( "feature" => $childMenuItems[$c] );
5011  }
5012 // need to build an array of feature => $childMenuItems[x]
5013 // and put all into an array pointing to thisEntry
5014  $groupEntry = array( "dispname" => $groupDisplayName,
5015  "disporder" => $groupDisplayOrder,
5016  $childList );
5017 
5018  $thisEntry = array( "group" => $groupEntry );
5019  } else if ( $menuElement["menu_item_type"] == "S" ) {
5020  // stand-alone menu item (not in a group) with no children
5021 
5022  // get the feature code
5023  $menuFeatureCode = HCU_array_key_value( 'menu_feature_code', $menuElement );
5024  $desktopScript = HCU_array_key_value( 'menu_script', $menuElement );
5025 
5026  $featureCode = _GetAppMenuFeatureCode( $menuFeatureCode, $desktopScript );
5027 
5028  if ( $featureCode == "unknown" ) {
5029  // don't pass along the unknowns
5030  continue;
5031  }
5032 
5033  // transate target to new window
5034  $target = HCU_array_key_value( 'menu_target', $menuElement ) == 1 ? "browser" : "webview";
5035 
5036  $menuItem = array(
5037  "featurecode" => $featureCode,
5038  "menuname" => HCU_array_key_value( 'menu_display_name_en_US', $menuElement ),
5039  "menuid" => HCU_array_key_value( 'menu_item_id', $menuElement ),
5040  "disporder" => HCU_array_key_value( 'menu_display_order', $menuElement ),
5041  "icon" => HCU_array_key_value( 'menu_icon_name', $menuElement ),
5042  "extraparam" => urlencode( HCU_array_key_value( 'menu_extra_param', $menuElement ) ),
5043  "addcu" => HCU_array_key_value( 'menu_add_cu', $menuElement ),
5044  "target" => $target );
5045 
5046  // special handling for certain features
5047  if ( $featureCode == "sso" ) {
5048  $menuItem["ssourl"] = urlencode( HCU_array_key_value( 'menu_script', $menuElement ) );
5049  }
5050 
5051  $thisEntry = array( "feature" => $menuItem );
5052  }
5053 
5054  // add the entry to the return structure
5055  $returnMenuStructure[] = $thisEntry;
5056  }
5057 
5058  return $returnMenuStructure;
5059 } // end _BuildMenuStructure
5060 
5061 
5062 // Build the menu structure the apps will use for the full menu. The menu
5063 // items need to be in display order so the resultant XML is also in order.
5064 // The resultant structure needs to be correctly formatted in the XML returned
5065 // to the app (so it might look a little weird).
5066 function _BuildFullMenuStructure( $pHBEnv, $pMenuArray ) {
5067  $returnMenuStructure = array();
5068 
5069  // Headers elements start a tag and then call routine to build children.
5070  // Stand-alone elements build a tag.
5071  for ( $i = 0; $i < count ( $pMenuArray ); $i++ ) {
5072  $menuElement = $pMenuArray[$i];
5073 
5074  // remove some info that shouldn't be returned
5075  unset($menuElement["menu_display_name_en_US"]);
5076  unset($menuElement["menu_display_name_es_US"]);
5077  unset($menuElement["menu_display_name_pl_US"]);
5078  // there is a new element, menu_new_window
5079  unset($menuElement["menu_target"]);
5080 
5081  // do some encoding
5082  $menuElement["menu_display_name"] = urlencode($menuElement["menu_display_name"]);
5083  $menuElement["menu_extra_param"] = urlencode($menuElement["menu_extra_param"]);
5084 
5085  $thisEntry = array();
5086  if ( $menuElement["menu_item_type"] == "H" ) {
5087  // remove some info that shouldn't be returned
5088  unset($menuElement["menu_feature_code"]);
5089  unset($menuElement["menu_parent_id"]);
5090  unset($menuElement["menu_script"]);
5091  unset($menuElement["menu_extra_param"]);
5092  unset($menuElement["menu_add_cu"]);
5093 
5094  // get the menu id to find the children
5095  $parentItemId = HCU_array_key_value( 'menu_item_id', $menuElement );
5096 
5097  $childMenuItems = array();
5098  $childMenuArray = $menuElement["menu_children"];
5099  for ( $c = 0; $c < count( $childMenuArray ); $c++ ) {
5100  $childItem = $childMenuArray[$c];
5101 
5102  // remove some info that shouldn't be returned
5103  unset($childItem["menu_display_name_en_US"]);
5104  unset($childItem["menu_display_name_es_US"]);
5105  unset($childItem["menu_display_name_pl_US"]);
5106  // there is a new element, menu_new_window
5107  unset($childItem["menu_target"]);
5108 
5109  // do some encoding
5110  $childItem["menu_display_name"] = urlencode($childMenuArray[$c]["menu_display_name"]);
5111  $childItem["menu_extra_param"] = urlencode($childMenuArray[$c]["menu_extra_param"]);
5112 
5113  // add to the new array
5114  $childMenuItems[]["feature"] = $childItem;
5115  }
5116 
5117  // replace the original so the changes we just did are present in what is being returned
5118  $menuElement["menu_children"] = $childMenuItems;
5119 
5120  $thisEntry = array( "group" => $menuElement );
5121  } else if ( $menuElement["menu_item_type"] == "S" ) {
5122  // stand-alone menu item (not in a group) with no children
5123  $thisEntry = array( "feature" => $menuElement );
5124  }
5125 
5126  // add the entry to the return structure
5127  $returnMenuStructure[] = $thisEntry;
5128  }
5129 
5130  return $returnMenuStructure;
5131 } // end _BuildFullMenuStructure
5132 
5133 // Build an array of feature menu items that are in the group identified by the
5134 // parent id. This routine only handled menu item types of "D" (detail).
5135 function _BuildChildMenu( $pHBEnv, $pMenuArray, $pParentId ) {
5136 
5137  // loop through the menu gathering the children of the given parent id
5138  $childMenuItems = array();
5139  for ( $i = 0; $i < count( $pMenuArray ); $i++ ) {
5140  $menuItem = $pMenuArray[$i];
5141 
5142  $menuItemType = HCU_array_key_value( 'menu_item_type', $menuItem );
5143  $menuParentId = HCU_array_key_value( 'menu_parent_id', $menuItem );
5144 
5145  if ( $menuItemType != "D" || $menuParentId != $pParentId ) {
5146  continue;
5147  }
5148 
5149  // get the feature code
5150  $menuFeatureCode = HCU_array_key_value( 'menu_feature_code', $menuItem );
5151  $desktopScript = HCU_array_key_value( 'menu_script', $menuItem );
5152 
5153  $featureCode = _GetAppMenuFeatureCode( $menuFeatureCode, $desktopScript );
5154 
5155  if ( $featureCode == "unknown" ) {
5156  // don't pass along the unknowns
5157  continue;
5158  }
5159 
5160  // transate target to new window
5161  $target = HCU_array_key_value( 'menu_target', $menuItem ) == 1 ? "browser" : "webview";
5162 
5163  $childItem = array(
5164  "featurecode" => $featureCode,
5165  "menuname" => HCU_array_key_value( 'menu_display_name_en_US', $menuItem ),
5166  "menuid" => HCU_array_key_value( 'menu_item_id', $menuItem ),
5167  "disporder" => HCU_array_key_value( 'menu_display_order', $menuItem ),
5168  "icon" => HCU_array_key_value( 'menu_icon_name', $menuItem ),
5169  "extraparam" => urlencode( HCU_array_key_value( 'menu_extra_param', $menuItem ) ),
5170  "addcu" => HCU_array_key_value( 'menu_add_cu', $menuItem ),
5171  "target" => $target );
5172 
5173  // special handling for certain features
5174  if ( $featureCode == "sso" || $featureCode == "sso-estmt" ||
5175  $featureCode == "sso-mrdc" || $featureCode == "sso-mbillpay" ||
5176  $featureCode == "sso-pfm" || $featureCode == "sso-loanapp" ) {
5177  $childItem["ssourl"] = urlencode( HCU_array_key_value( 'menu_script', $menuItem ) );
5178 
5179  // determine the sso type based on the feature code extension
5180  $parts = explode( "-", $featureCode );
5181  $type = count( $parts ) > 1 ? $parts[1] : "";
5182  $childItem["ssotype"] = $type;
5183 
5184  // make them all "sso"
5185  $childItem["featurecode"] = "sso";
5186  }
5187 
5188  $childMenuItems[] = $childItem;
5189  }
5190 
5191  return $childMenuItems;
5192 } // end _BuildChildMenu
5193 
5194 // Determine the feature code based on the menu feature code and the desktop filename.
5195 // The Menu Feature Code is what the whole system uses for features; the Desktop Filename
5196 // is the script file called on the desktop. The return value is the code the app uses.
5197 // NOTE: SSOs use different feature codes to help differentiate the type of sso.
5198 function _GetAppMenuFeatureCode( $pMenuFeatureCode, $pDesktopScript ) {
5199  $appFeatureCode = "unknown";
5200 
5201  switch ( strtoupper( $pMenuFeatureCode ) ) {
5202  case "ACHCOL":
5203  // note: ach partner maintenance is implied with either
5204  $appFeatureCode = "achcol";
5205  break;
5206  case "ACHPMT":
5207  // note: ach partner maintenance is implied with either
5208  $appFeatureCode = "achpmt";
5209  break;
5210  case "ACHPYRL":
5211  // not handled at this point
5212  break;
5213  case "ALERT":
5214  $appFeatureCode = "alerts";
5215  break;
5216  case "BASIC":
5217  // there are a lot of choices here that distill down to a few choices
5218  switch( $pDesktopScript ) {
5219  case "hcuAccounts.prg":
5220  // NOTE: History is implied with balances on the app
5221  $appFeatureCode = "balances";
5222  break;
5223  case "hcuForms.prg":
5224  $appFeatureCode = "forms";
5225  break;
5226  case "hcuProfilePwd.prg":
5227  case "hcuProfileSecurity.prg":
5228  case "hcuProfileDesc.prg":
5229  case "hcuProfileAlias.prg":
5230  case "hcuProfileEmail.prg":
5231  $appFeatureCode = "settings";
5232  break;
5233  case "hcuDisclosures.prg":
5234  case "hcuHistory.prg":
5235  // not handled at this point
5236  break;
5237  default:
5238  // at this point assume an SSO was selected
5239  $appFeatureCode = "sso";
5240  break;
5241  }
5242  break;
5243  case "ESTMT":
5244  if ( $pDesktopScript == "hcuStatement.prg" ) {
5245  $appFeatureCode = "estatements";
5246  } else {
5247  // at this point assume an SSO was selected
5248  $appFeatureCode = "sso-estmt";
5249  }
5250  break;
5251  case "MBILLPAY":
5252  if ( $pDesktopScript == "hcuMobilePay.prg" ) {
5253  $appFeatureCode = "mobilepay";
5254  } else {
5255  // at this point assume an SSO was selected
5256  $appFeatureCode = "sso-mbillpay";
5257  }
5258  break;
5259  case "MRDC":
5260  if ( $pDesktopScript == "hcuConnect.prg" ) {
5261  // at this point assume an SSO was selected
5262  $appFeatureCode = "sso-mrdc";
5263  } else {
5264  // otherwise, it is a native plug-in
5265  $appFeatureCode = "mrdc";
5266  }
5267  break;
5268  case "PFM":
5269  $appFeatureCode = "sso-pfm";
5270  break;
5271  case "ONLINEAPP":
5272  $appFeatureCode = "sso-loanapp";
5273  break;
5274  case "SECUREMSG":
5275  $appFeatureCode = "messages";
5276  break;
5277  case "STOP":
5278  break;
5279  case "TRN":
5280  if ( $pDesktopScript == "hcuTransfer.prg" ) {
5281  $appFeatureCode = "transfers";
5282  } else if ( $pDesktopScript == "hcuUserActivity.prg" ) {
5283  $appFeatureCode = "activity";
5284  }
5285  break;
5286  case "TRNEXT":
5287  $appFeatureCode = "tranext";
5288  break;
5289  case "TRNM2M":
5290  $appFeatureCode = "tranm2m";
5291  break;
5292  case "TRNSCHED":
5293  $appFeatureCode = "transched";
5294  break;
5295  case "TRNWIRE":
5296  $appFeatureCode = "tranwire";
5297  break;
5298  case "BILLPAY":
5299  case "SMS":
5300  case "RDC":
5301  // these are not mobile features
5302  break;
5303  }
5304 
5305  return $appFeatureCode;
5306 
5307 } // _GetAppMenuFeatureCode
5308 
5309 function MakeMFAKey($HB_ENV) {
5310 
5311  $mfaExpires = date("Ymd", time() + (94 * 86400)); # 94 days
5312 
5313  $mbrMfaQuest = HCU_MFADecode(HCU_JsonDecode($HB_ENV['mfaquest']));
5314  $mfaMode = (intval($HB_ENV['Fset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')));
5315  // if we set a date here, would have to update userrec to store it
5316  // $mfadate = (empty($mbrMfaQuest['mfadate']) ? date("Ymd") : $mbrMfaQuest['mfadate']);
5317  $mfadate = HCU_array_key_exists("mfadate", $mbrMfaQuest) ? $mbrMfaQuest['mfadate'] : "";
5318  $MFAKey = hash_hmac('sha384',GetDeviceCookieContentString(),trim($HB_ENV['password']) . trim(strtolower($HB_ENV['savemail'])) . trim(strtolower($HB_ENV['confidence'])) . $mfaMode . $mfadate);
5319 
5320  return "{$mfaExpires}{$MFAKey}";
5321 }
5322 
5323 function IsValidMFAKey($mfaKey, $userrec) {
5324  $today = date("Ymd");
5325 
5326  $mfaExpires = substr($mfaKey,0,8);
5327  $mfaHash = substr($mfaKey,8);
5328 
5329  $mfaMode = (intval($userrec['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')));
5330  $mfadate = HCU_array_key_exists("mfadate", $userrec) ? $userrec['mfadate'] : "";
5331  $cookiecontent = hash_hmac('sha384',GetDeviceCookieContentString(),trim($userrec['passwd']) . trim(strtolower($userrec['email'])) . trim(strtolower($userrec['confidence']))
5332  . $mfaMode . $mfadate);
5333  if ($cookiecontent == $mfaHash && $mfaExpires > $today) {
5334  $return_val = true;
5335  } else {
5336  $return_val = false;
5337  }
5338  return $return_val;
5339 }
5340 
5341 /* Test if the given device cookie is valid. It will be in the form
5342  * name|content|expire where expire is an epoch. First check the expiration
5343  * and then that the name and value match.
5344  */
5345 function IsValidAppDeviceCookie($cu, $deviceCookie, $userrec) {
5346  $success = false;
5347 
5348  // first check the basics - expiration, existence
5349  $deviceCookieParts = explode("|", $deviceCookie);
5350  if ( $deviceCookieParts[2] < time() ||
5351  empty( $deviceCookieParts[0] ) ||
5352  empty( $deviceCookieParts[1] ) ) {
5353  return false;
5354  }
5355 
5356  $mfaMode = (intval($userrec['flagset3'] & GetFlagsetValue('CU3_MFA_AUTHCODE')));
5357  $mfaDate = HCU_array_key_value("mfadate", $userrec);
5358 
5359  $cookieParams = array ( "cu" => trim($cu),
5360  "user_name" => $userrec['user_name'],
5361  "saved_pass" => $userrec['passwd'],
5362  "saved_email" => $userrec['email'],
5363  "saved_confidence" => $userrec['confidence'],
5364  "mfa_mode" => $mfaMode,
5365  "mfa_date" => $mfaDate,
5366  "persists_time" => 0 // are not actually creating a cookie for use
5367  );
5368 
5369  $cookieInfo = CreateDeviceCookie( $cookieParams);
5370 
5371  if ( ($cookieInfo["name"] == $deviceCookieParts[0]) &&
5372  ($cookieInfo["content"] == $deviceCookieParts[1]) ) {
5373  $success = true;
5374  } else {
5375  $success = false;
5376  }
5377 
5378  return $success;
5379 }
5380 
5381 function LogFail($dbh, $HB_ENV, $inPost, $failbit) {
5382  $updstat = UpdateMemberFailedLogin($dbh, $HB_ENV['cu'], $HB_ENV['user_name'], $failbit);
5383  $p_meta = array('UA' => $_SERVER['HTTP_USER_AGENT']);
5384  $p_hbenv = array('Cu' => $inPost['ORG'], 'Uid' => $HB_ENV['Uid'], 'user_name' => $HB_ENV['user_name']);
5385  TrackUserLogin($dbh, $p_hbenv, $HB_ENV['platform'], $failbit, $_SERVER['REMOTE_ADDR'], $p_meta);
5386 }
5387 
5388 function LogPass($dbh, &$HB_ENV){
5389  # if not online, set the logtrack parameters to NOT decrement the remaining logins
5390  $must = ($HB_ENV['offline'] != 'N' || ($HB_ENV['forceupdate'] & 29) == 0 ? 'N' : 'Y');
5391  $tomorrow = date('Y-m-d', mktime(0, 0, 0, date("m"), date("d") + 1, date("Y")));
5392  $pchange = ($HB_ENV['offline'] != 'N' ? $tomorrow : $HB_ENV['pwchange']);
5393  $adjust = ($must == 'Y' ? 1 : 0);
5394  # and fix the corresponding value in HB_ENV
5395  $HB_ENV['Ffremain']-=$adjust;
5396  // The challenge key must be reset to 0 for SUCCESSFUL LOGINS
5397  // and the sac code and expiration must be reset empty for SUCCESSFUL LOGINS
5398  $HB_ENV['MFA']['challenge'] = 0;
5399  $HB_ENV['MFA']['authcode']='';
5400  $HB_ENV['MFA']['authexpires']='';
5401 
5402 // $GLOBALS['HB_ENV']['SYSENV']['logger']->info("UpdateMemberLoginTrack(dbh, {$HB_ENV['cu']}, {$HB_ENV['user_name']}, $must, $pchange, {$HB_ENV['platform']}, {$HB_ENV['MFA']});");
5403  $updstat = UpdateMemberLoginTrack($dbh, $HB_ENV['cu'], $HB_ENV['user_name'], $must, $pchange, $HB_ENV['platform'], $HB_ENV['MFA']);
5404  $p_meta = array('UA' => $_SERVER['HTTP_USER_AGENT']);
5405  TrackUserLogin($dbh, $HB_ENV, $HB_ENV['platform'], 0, $_SERVER['REMOTE_ADDR'], $p_meta);
5406 
5407 }
5408 function SAC_destination($dbh, $HB_ENV, $userrec) {
5409  $reply_arr = array('STATUS' => array('CODE' =>3000,'SEVERITY' => 'ERROR'),
5410  'DTSERVER' => date('YmdHis') );
5411 
5412  $MemberContacts_ary = GetUserContacts($dbh, $HB_ENV, $userrec);
5413  $reply_arr['MFA']['AUTHREQ'] = 'MFS';
5414  $reply_arr['MFA']['MFABUNDLE'] = $HB_ENV['mfaBundle'];
5415  $reply_arr['MFA']['EMLLABEL'] = $HB_ENV['MC']->msg('Email to', HCU_DISPLAY_AS_HTML);
5416  if (is_array($MemberContacts_ary['EMAIL']) && sizeof($MemberContacts_ary['EMAIL'])) {
5417  foreach ($MemberContacts_ary['EMAIL'] as $ckey => $cval) {
5418  $ckey = str_replace(array("+", "/", "="), array("-", "_", "."), $ckey);
5419  $reply_arr['MFA']['EMLLIST'][]['EMAIL']= array('eSelect'=>$ckey, 'eDisplay' =>$cval);
5420  }
5421  }
5422  if (is_array($MemberContacts_ary['SMS']) && sizeof($MemberContacts_ary['SMS'])) {
5423  $reply_arr['MFA']['SMSLABEL'] = $HB_ENV['MC']->msg('Text to', HCU_DISPLAY_AS_HTML);
5424  foreach ($MemberContacts_ary['SMS'] as $ckey => $cval) {
5425  $ckey = str_replace(array("+", "/", "="), array("-", "_", "."), $ckey);
5426  $reply_arr['MFA']['SMSLIST'][]['SMS']= array('eSelect'=>$ckey, 'eDisplay' =>$cval);
5427  }
5428  }
5429 
5430  $reply_arr['MFA']['haveSAC'] = HCU_array_key_value('GotIt', $MemberContacts_ary );
5431 
5432  return $reply_arr;
5433 }
5434 
5435 function PWD_prompt($dbh, $HB_ENV) {
5436  $reply_arr = array('STATUS' => array('CODE' =>3000,'SEVERITY' => 'ERROR'),
5437  'DTSERVER' => date('YmdHis') );
5438 
5439  # needs to send
5440  # 'forgot password' link
5441  # confidence word
5442 
5443  $reply_arr['MFA']['AUTHREQ'] = 'MFP';
5444  $reply_arr['MFA']['MFABUNDLE'] = $HB_ENV['mfaBundle'];
5445  $reply_arr['MFA']['PROMPT'] = $HB_ENV['MC']->msg('Login Enter Password');
5446  $reply_arr['MFA']['CONFIDENCE'] = $HB_ENV['confidence'];
5447 
5448  if ($HB_ENV['flagset'] & $GLOBALS['CU_MEMRESET']){
5449  $reply_arr['MFA']['FORGOTLINK'] = $HB_ENV['loginpath'] . '/hcuResetPwd.prg?' . $HB_ENV['cuquery'];
5450  $reply_arr['MFA']['FORGOTLABEL'] = $HB_ENV['MC']->msg('Forgot your password');
5451  }
5452 
5453  return $reply_arr;
5454 }
5455 
5456 function EML_prompt($dbh, $HB_ENV) {
5457  $reply_arr = array('STATUS' => array('CODE' =>3000,'SEVERITY' => 'ERROR'),
5458  'DTSERVER' => date('YmdHis') );
5459  $reply_arr['MFA']['AUTHREQ'] = 'EML';
5460  $reply_arr['MFA']['MFABUNDLE'] = $HB_ENV['mfaBundle'];
5461  $reply_arr['MFA']['PROMPT'] = $HB_ENV['MC']->msg('Confirm Email Address');
5462 
5463  return $reply_arr;
5464 }
5465 function SAC_prompt($dbh, $HB_ENV) {
5466  $reply_arr = array('STATUS' => array('CODE' =>3000,'SEVERITY' => 'ERROR'),
5467  'DTSERVER' => date('YmdHis') );
5468  $reply_arr['MFA']['AUTHREQ'] = 'MFC';
5469  $reply_arr['MFA']['MFABUNDLE'] = $HB_ENV['mfaBundle'];
5470  $reply_arr['MFA']['PROMPT'] = $HB_ENV['MC']->msg('Enter Access Code');
5471 
5472  return $reply_arr;
5473 }
5474 function checkBundle($mode, $mfaBundle, $inPost) {
5475 
5476  switch ($mode) {
5477  case 'TIME':
5478  # special case to check the bundle age
5479  $built = HCU_array_key_value('BUILDTIME', $mfaBundle);
5480  if ( empty($built) || (time() - $built) > 900 ) {
5481  $returnBundle = false;
5482  } else {
5483  $returnBundle = true;
5484  }
5485  break;
5486  case 'EML':
5487  # previous mode is MFA,
5488  # bundle contains USERID
5489  # Add MFA_E
5490 
5491  if ( HCU_array_key_value('mode', $mfaBundle) !== 'MFA' ||
5492  HCU_array_key_value('USERID',$mfaBundle) !== HCU_array_key_value('USERID',$inPost) ) {
5493  $returnBundle = false;
5494  } else {
5495  $returnBundle = true;
5496  }
5497  break;
5498  case 'MFQ':
5499  case 'MFS':
5500  # previous mode is EML
5501  # bundle contains USERID, MFA_E
5502  # Add MFQ completion marker
5503  if ( (HCU_array_key_value('mode', $mfaBundle) !== 'EML' && HCU_array_key_value('mode', $mfaBundle) !== 'MFS') ||
5504  HCU_array_key_value('USERID',$mfaBundle) !== HCU_array_key_value('USERID',$inPost) ||
5505  !HCU_array_key_exists('MFA_E',$mfaBundle) ) {
5506  $returnBundle = false;
5507  } else {
5508  $returnBundle = true;
5509  }
5510  break;
5511  case 'MFC':
5512  # previous mode is MFS, or maybe EML if they already had a code
5513  # bundle contains USERID, MFA_E, MFS_Deliver
5514  # Add SAC completion marker
5515  if ( (HCU_array_key_value('mode', $mfaBundle) !== 'MFS' && HCU_array_key_value('mode', $mfaBundle) !== 'EML' ) ||
5516  HCU_array_key_value('USERID',$mfaBundle) !== HCU_array_key_value('USERID',$inPost) ||
5517  !HCU_array_key_exists('MFA_E',$mfaBundle) ||
5518  (HCU_array_key_value('mode', $mfaBundle) == 'MFS' && !HCU_array_key_exists('MFS_Deliver',$mfaBundle)) ) {
5519  $returnBundle = false;
5520  } else {
5521  $returnBundle = true;
5522  }
5523  break;
5524  case 'MFP':
5525  # previous mode is MFQ or MFC or EML or MFA
5526  # bundle contains USERID, MFA_E,
5527  # and either MFQ marker or SAC marker
5528  if (
5529  !((HCU_array_key_value('mode', $mfaBundle) === 'MFQ' && HCU_array_key_exists('haveMFQ',$mfaBundle)) ||
5530  (HCU_array_key_value('mode', $mfaBundle) === 'MFC' && HCU_array_key_exists('haveSAC',$mfaBundle)) ||
5531  HCU_array_key_value('mode', $mfaBundle) === 'EML' || HCU_array_key_value('mode', $mfaBundle) === 'MFA')
5532  || HCU_array_key_value('USERID',$mfaBundle) !== HCU_array_key_value('USERID',$inPost) ||
5533  !HCU_array_key_exists('MFA_E',$mfaBundle)
5534  ) {
5535 
5536 // if ( (HCU_array_key_value('mode', $mfaBundle) !== 'MFQ' && HCU_array_key_value('mode', $mfaBundle) !== 'MFC' ) ||
5537 // HCU_array_key_value('USERID',$mfaBundle) !== HCU_array_key_value('USERID',$inPost) ||
5538 // !HCU_array_key_exists('MFA_E',$mfaBundle) ||
5539 // (!HCU_array_key_exists('haveSAC',$mfaBundle) && !HCU_array_key_exists('haveMFQ',$mfaBundle) ) ) {
5540  $returnBundle = false;
5541  } else {
5542  $returnBundle = true;
5543  }
5544  break;
5545  default:
5546  # unexpected $mode - throw error
5547  $returnBundle = false;
5548  break;
5549  }
5550  return $returnBundle;
5551 }
5552 function createBundle($Cu, $mfaBundle) {
5553  try {
5554  $mfaBundle = HCU_PayloadEncode($Cu,$mfaBundle);
5555  $mfaBundle = str_replace(array("+", "/", "="), array("-", "_", "."), $mfaBundle);
5556  } catch (Exception $e) {
5557  $mfaBundle='';
5558  }
5559 
5560  return $mfaBundle;
5561 }
5562 function openBundle($Cu, $mfaBundle) {
5563  try {
5564 
5565  $mfaBundle = str_replace(array("-", "_", "."), array("+", "/", "="), $mfaBundle);
5566  $mfaBundle = HCU_PayloadDecode($Cu, $mfaBundle);
5567 
5568  } catch (Exception $e) {
5569  $mfaBundle=array();
5570  }
5571  return $mfaBundle;
5572 }
5573 /* This is the original OdyTxPost that works with existing apps. At some point it can go away but
5574  * need to wait until most apps are updated to do that.
5575  */
5576 function OdyTxPost ($dbh, $HB_ENV, $inPost, $MC) {
5577  try {
5578  $errorMessage = array();
5579 
5580  // first need to check input, make sure we have something to post
5581  // inPost should contain FACCTID TACCTID AMOUNT & optional TRMEMO
5582  // these are needed in a variety of places so get them now
5583  $sourceParts = isset( $inPost['FACCTID'] ) ? explode( "|", $inPost['FACCTID'] ) : array(); // eg "D|1103|10|0
5584  $destParts = isset( $inPost['TACCTID'] ) ? explode( "|", $inPost['TACCTID'] ) : array();
5585 
5586  // check if feature is regular, external, or M2M transfer
5587  if ( (isset( $sourceParts[0] ) && $sourceParts[0] === "X") ||
5588  (isset( $destParts[0] ) && $destParts[0] === "X") ) {
5589  $transferFeatureCode = FEATURE_EXTERNAL_TRANSFERS;
5590  } else if ( isset( $destParts[0] ) && $destParts[0] === "M" ) {
5591  $transferFeatureCode = FEATURE_M2M_TRANSFERS;
5592  } else {
5593  $transferFeatureCode = FEATURE_TRANSFERS;
5594  }
5595  # UGLY hidden dependency... SubmitTransfer function expects this in
5596  # the post array so load it here.
5597  $HB_ENV['HCUPOST']['feature_code'] = $transferFeatureCode;
5598 
5599  $permissionInputs = array( "feature" => $transferFeatureCode );
5600 
5601  // check if user has create rights
5602  $accessRights = Perm_AccessRights( $dbh, $HB_ENV, $permissionInputs );
5603  if ( !HCU_array_key_value( "create", $accessRights ) ) {
5604  $errorMessage[] = $MC->msg('Rights not set', HCU_DISPLAY_AS_HTML);
5605  throw new Exception ( HCU_JsonEncode($errorMessage) );
5606  }
5607 
5608  // get some allowed amounts for client-side validation
5609  $permissionInputs = array( "feature" => $transferFeatureCode );
5610  $limits = Perm_GetValidationLimits( $dbh, $HB_ENV, $permissionInputs );
5611 
5612  if ( $limits === false ) {
5613  // error occurred - assume count of zero
5614  $allowedAmount = 0;
5615  } else {
5616  $allowedAmount = floatval( $limits["amount_per_transaction"] );
5617  }
5618 
5619  /*
5620  * apps currently support only immediate transfers to previously defined
5621  * accounts. Trying to maintain the structure here for future growth,
5622  * but most of these don't apply at this time. October '17
5623  */
5624 
5625  $txData = array('txFrequency' => "OneTime",
5626  'txFromMember' => $sourceParts[1],
5627  'txFromSuffix' => $inPost['FACCTID'],
5628  'txFromType' => "",
5629  'txToMember' => $destParts[1],
5630  'txToSuffix' => $inPost['TACCTID'],
5631  'txToType' => "",
5632  'txToMisc1' => "",
5633  'txMemAccount' => null,
5634  'txMemName' => null,
5635  'txMemType' => null,
5636  'txCode' => null, # this needs to get set here, somehow?
5637  'txAmount' => HCU_array_key_value('AMOUNT',$inPost),
5638  'txMemo' => HCU_array_key_value('TRMEMO',$inPost),
5639  'txFrequencyCount' => 0,
5640  'txContinue' => null,
5641  'txDateStart' => date("m/d/Y"),
5642  'txDateEnd' => null,
5643  'txStatus' => null,
5644  'txPmtComment' => null,
5645  'txDeposit' => null,
5646  'txId' => 0,
5647  'txOption' => "Immediate",
5648  "feature_code" => $HB_ENV["HCUPOST"]["feature_code"]);
5649 
5650  // ** CHECK
5651  // ** - recurring transfers are allowed
5652  $txRecurringAllowed = (($HB_ENV['flagset2'] & GetFlagsetValue("CU2_PROCRECUR")) === GetFlagsetValue("CU2_PROCRECUR"));
5653  // ** - is payment or transfer
5654  $txIsPayment = ($destParts[0] == 'O' && substr( $destParts[2], 0, 1 ) === "P");
5655 
5656  // ** CHECK
5657  // ** - Immediate
5658  // ** - recurring not supported from apps, not addressed here at this time
5659  if (!$txRecurringAllowed || $txData['txOption'] === "Immediate") {
5660 
5661 // $HB_ENV["SYSENV"]["logger"]->info(__LINE__ . " ready to validate");
5662 
5663  // ** VALIDATE - data was sanitized already
5664  $txValidateTransfer = ValidateTransfer( $HB_ENV, $dbh, $MC, $txData );
5665 
5666  if ( $txValidateTransfer['status']['code'] !== "000" ) {
5667 
5668  for ( $i = 0; $i < count( $txValidateTransfer["status"]["errors"] ); $i++ ) {
5669  $errorMessage[] = $txValidateTransfer["status"]["errors"][$i]["message"];
5670  }
5671 
5672  throw new Exception ( HCU_JsonEncode($errorMessage) );
5673  }
5674 
5675 
5676 // $htmlTransferAmount = $inPost['AMOUNT'];
5677  # should be validated amount, not the raw inPost value
5678  $htmlTransferAmount = HCU_array_key_value('txAmount',$txValidateTransfer['data']);
5679 
5680  if ( $htmlTransferAmount > $allowedAmount ) {
5681  // error occurred - cannot allow operation - just say there was a limit error
5682  $errorMessage[] =$MC->msg('Perm Limit - Request over authorized limit', HCU_DISPLAY_AS_HTML);
5683  throw new Exception ( HCU_JsonEncode($errorMessage) );
5684  }
5685 
5686  // get some allowed amounts for validation
5687  $permissionInputs = array( "feature" => $transferFeatureCode );
5688  $permissionInputs["amount"] = $htmlTransferAmount;
5689  # these should probably come from the validated results,
5690  # but ValidateTransfer is transforming some of the data
5691  # and may be confusing acctid (delimited string with several values)
5692  # with the account number/account suffix.
5693  $permissionInputs["account"] = $sourceParts[1];
5694  $permissionInputs["accounttype"] = $sourceParts[2];
5695 
5696  $return = Perm_CheckLimits( $dbh, $HB_ENV, $permissionInputs );
5697  if ( !$return || ($return["status"]["code"] !== "000") ) {
5698  // error occurred - cannot allow operation - just say there was a limit error
5699  $errorMessage[] = Perm_GetLimitErrDesc($MC, $return["status"]["code"]);
5700  throw new Exception ( HCU_JsonEncode($errorMessage) );
5701  }
5702 
5703  // ** Use the results from ValidateTransfer and pass into SubmitTransfer
5704  $submitTransferResult = SubmitTransfer( $dbh, $HB_ENV, $MC, $txValidateTransfer['data'], $aryTransferResults );
5705 
5706 
5707  if ( $submitTransferResult === false ) {
5708  // error occurred - cannot allow operation
5709  $errorMessage[] = $MC->msg("Transfer Error", HCU_DISPLAY_AS_HTML);
5710  }
5711 //$HB_ENV["SYSENV"]["logger"]->info(__LINE__ . " aryTransferResults " . print_r($aryTransferResults,true) . " submitTransferResult " . print_r($submitTransferResult,true));
5712 
5713  // see if the recording returned any errors
5714  if ( $aryTransferResults["status"]["code"] != "000" ) {
5715  if ( count( $submitTransferResult["status"]["errors"] ) > 0 ) {
5716  for ( $i = 0; $i < count( $submitTransferResult["status"]["errors"] ); $i++ ) {
5717  $errorMessage[] = $submitTransferResult["status"]["errors"][$i];
5718  }
5719  }
5720  }
5721 
5722  // check if either of the two possible ways to return errors happened
5723  if ( count( $errorMessage ) > 0 ) {
5724  if ($txData['txFrequency'] != 'OneTime') {
5725  // ** If we have an error from the POST routine AND the member tried to add
5726  // * a repeating, just mention that here..
5727  $errorMessage[] = $MC->msg("Repeating transfer not saved", HCU_DISPLAY_AS_RAW);
5728  }
5729 
5730  throw new Exception ( HCU_JsonEncode($errorMessage) );
5731  }
5732  // transfer record saved, hang on to the results
5733  $retStatus_ary['txn'] = $aryTransferResults['txn'];
5734 
5735  // now see if confirmation not required
5736  $confirmationRequired = Perm_CheckConfirmReq($dbh, $HB_ENV, $permissionInputs);
5737  if (!$confirmationRequired) {
5738  // since can self-approve do that now (returning TX_Post data)
5739  $entryId = $aryTransferResults["txn"]["trans_id"]; // result from SubmitTransfer
5740 
5741  if ( !ApproveTransfer( $dbh, $HB_ENV, $MC, $entryId, $aryApprovalResults ) ) {
5742  $errorMessage[] = $MC->msg("Trans approval failure", HCU_DISPLAY_AS_HTML);
5743  throw new Exception ( HCU_JsonEncode($errorMessage) );
5744  }
5745 
5746  // for internal transfers, if it was self-approved it can be processed right away
5747  if ( $transferFeatureCode == FEATURE_TRANSFERS ||
5748  $transferFeatureCode == FEATURE_M2M_TRANSFERS ) {
5749  // * TRADITIONAL TRANSFER
5750  if ( !ProcessTransfer( $dbh, $HB_ENV, $MC, $entryId, $aryProcessResults ) ) {
5751  $errorMessage[] = $MC->msg("Trans processing failure", HCU_DISPLAY_AS_HTML);
5752 
5753  if ( count( $aryProcessResults["status"]["errors"] ) > 0 ) {
5754  for ( $i = 0; $i < count( $aryProcessResults["status"]["errors"] ); $i++ ) {
5755  $errorMessage[] = $aryProcessResults["status"]["errors"][$i];
5756  }
5757  }
5758  throw new Exception ( HCU_JsonEncode($errorMessage) );
5759  }
5760  }
5761 
5762  $retStatus_ary['txn']['status'] = $MC->msg("Transfer posted", HCU_DISPLAY_AS_RAW);
5763  } else {
5764  // return message about awaiting approval, and just enough data for confirmation
5765  $retStatus_ary['txn']['status'] = $MC->msg("Transfer submitted for confirmation", HCU_DISPLAY_AS_RAW);
5766  }
5767 
5768  // ** Successful transfer -- Need to return information..
5769  $retStatus_ary['status']['code']='0';
5770  $retStatus_ary['status']['severity']='INFO';
5771  }
5772  } catch(Exception $ex)
5773  {
5774  $retStatus_ary['status']['code']='999';
5775  $retStatus_ary['status']['severity']='ERROR';
5776  $retStatus_ary['status']['errors'] = HCU_JsonDecode( $ex->getMessage());
5777  $retStatus_ary['txn'] = array();
5778  }
5779 return $retStatus_ary;
5780 }
5781 
5782 
5783 /* This is the new routine to post regular or scheduled transfers to the server core code. It uses
5784  * the new PerformTransfer functionality that is shared for all platforms and can handle scheduled
5785  * or immediate transfers.
5786  */
5787 function TxPostAdvanced( $pHBEnv, $pDbh, $inPost, $pMC) {
5788  $retStatusAry = Array(
5789  'status' => Array('code'=>'000', 'errors' => Array()),
5790  'data' => '',
5791  'info' => ''
5792  );
5793 
5794  try {
5795  $errorMessage = array();
5796 
5797  // first need to check input, make sure we have something to post
5798  // inPost should contain FACCTID TACCTID AMOUNT & optional TRMEMO
5799  // these are needed in a variety of places so get them now
5800  $sourceParts = isset( $inPost['FACCTID'] ) ? explode( "|", $inPost['FACCTID'] ) : array(); // eg "D|1103|10|0
5801  $destParts = isset( $inPost['TACCTID'] ) ? explode( "|", $inPost['TACCTID'] ) : array();
5802 
5803  // check if feature is regular, external, or M2M transfer
5804  if ( (isset( $sourceParts[0] ) && $sourceParts[0] === "X") ||
5805  (isset( $destParts[0] ) && $destParts[0] === "X") ) {
5806  $transferFeatureCode = FEATURE_EXTERNAL_TRANSFERS;
5807  } else if ( isset( $destParts[0] ) && $destParts[0] === "M" ) {
5808  $transferFeatureCode = FEATURE_M2M_TRANSFERS;
5809  } else {
5810  $transferFeatureCode = FEATURE_TRANSFERS;
5811  }
5812  # UGLY hidden dependency... SubmitTransfer function expects this in
5813  # the post array so load it here.
5814  $pHBEnv['HCUPOST']['feature_code'] = $transferFeatureCode;
5815 
5816  $permissionInputs = array( "feature" => $transferFeatureCode );
5817 
5818  // check if user has create rights
5819  $accessRights = Perm_AccessRights( $pDbh, $pHBEnv, $permissionInputs );
5820  if ( !HCU_array_key_value( "create", $accessRights ) ) {
5821  $errorMessage[] = $pMC->msg('Rights not set', HCU_DISPLAY_AS_HTML);
5822  throw new Exception ( HCU_JsonEncode($errorMessage) );
5823  }
5824 
5825  // get some allowed amounts for client-side validation
5826  $permissionInputs = array( "feature" => $transferFeatureCode );
5827  $limits = Perm_GetValidationLimits( $pDbh, $pHBEnv, $permissionInputs );
5828 
5829  if ( $limits === false ) {
5830  // error occurred - assume count of zero
5831  $allowedAmount = 0;
5832  } else {
5833  $allowedAmount = floatval( $limits["amount_per_transaction"] );
5834  }
5835 
5836  // make sure we have a date
5837  $txDateStart = trim( HCU_array_key_value( "TXSTART", $inPost ) );
5838  if ( strlen( $txDateStart ) == 0 ) {
5839  $txDateStart = date("m/d/Y");
5840  }
5841 
5842  /*
5843  * Set up call to submit the transfer to the core routine.
5844  */
5845  $txData = array('txFrequency' => HCU_array_key_value('TXFREQ',$inPost),
5846  'txFromMember' => $sourceParts[1],
5847  'txFromSuffix' => $inPost['FACCTID'],
5848  'txToMember' => $destParts[1],
5849  'txToSuffix' => $inPost['TACCTID'],
5850  'txToMisc1' => "",
5851  'txMemAccount' => null,
5852  'txMemName' => null,
5853  'txMemType' => null,
5854  'txCode' => null, # this needs to get set here, somehow?
5855  'txAmount' => HCU_array_key_value('AMOUNT',$inPost),
5856  'txMemo' => HCU_array_key_value('TRMEMO',$inPost),
5857  'txContinue' => HCU_array_key_value('TXCONTINUE',$inPost),
5858  'txDateStart' => $txDateStart,
5859  'txDateEnd' => HCU_array_key_value('TXEND',$inPost),
5860  'txStatus' => null,
5861  'txPmtComment' => null, // not used on apps (for secure payment form)
5862  'txId' => 0, // new transaction being posted does not have an id yet
5863  "feature_code" => $transferFeatureCode // save the feature code for use elsewhere
5864  );
5865 
5866  // PerformTransfer does all the transactional work, so we just need to return what it returns
5867  $aryTransferResults = PerformTransfer( $pHBEnv, $txData, $pHBEnv["MC"] );
5868 
5869  if ( $aryTransferResults["status"]["code"] != "000" ) {
5870  if ( is_array( $aryTransferResults["status"]["errors"] ) ) {
5871  for ( $i = 0; $i < count( $aryTransferResults["status"]["errors"] ); $i++ ) {
5872  //need to handle both an array and/or a string being inside the "errors" element
5873  if ( is_array( $aryTransferResults["status"]["errors"][$i] ) ) {
5874  $errorMessage[] = $aryTransferResults["status"]["errors"][$i]["message"];
5875  } else {
5876  $errorMessage[] = $aryTransferResults["status"]["errors"][$i];
5877  }
5878  }
5879  } else {
5880  $errorMessage[] = $aryTransferResults["status"]["errors"];
5881  }
5882  }
5883 
5884  // check if either of the two possible ways to return errors happened
5885  if ( count( $errorMessage ) > 0 ) {
5886  if ($txData['txFrequency'] != 'OneTime') {
5887  // ** If we have an error from the POST routine AND the member tried to add
5888  // * a repeating, just mention that here..
5889  $errorMessage[] = $pMC->msg("Repeating transfer not saved", HCU_DISPLAY_AS_RAW);
5890  }
5891 
5892  throw new Exception ( HCU_JsonEncode($errorMessage) );
5893  }
5894 
5895  // ** Successful transfer -- Need to return information..
5896  $retStatusAry['status']['code']='0';
5897  $retStatusAry['status']['severity']='INFO';
5898 
5899  // the call to PerformTransfer will either return scheduled transfer info OR immediate transfer info, but not both.
5900 
5901  if ( HCU_array_key_exists( "repeat", $aryTransferResults["data"] ) && !is_array($aryTransferResults["data"]["repeat"])) {
5902  // scheduled tranfer data returns a message that doesn't quite work so use a different one
5903  $retStatusAry["txn"]["status"] = $pMC->msg( "Scheduled transfer was saved", HCU_DISPLAY_AS_RAW );
5904  } else {
5905  // regular transfer
5906  if ( HCU_array_key_exists( "txn", $aryTransferResults["data"] ) ) {
5907  $retStatusAry["txn"] = $aryTransferResults["data"]["txn"];
5908  }
5909 
5910  if ( HCU_array_key_exists( "posted", $aryTransferResults["data"] ) ) {
5911  $retStatusAry["txn"]["status"] = $aryTransferResults["data"]["posted"];
5912  } else {
5913  $retStatusAry["txn"]["status"] = $pMC->msg("Transfer posted", HCU_DISPLAY_AS_RAW);
5914  }
5915  }
5916  } catch(Exception $ex) {
5917  $retStatusAry['status']['code']='999';
5918  $retStatusAry['status']['severity']='ERROR';
5919  $retStatusAry['status']['errors'] = HCU_JsonDecode( $ex->getMessage());
5920  $retStatusAry['txn'] = array();
5921  }
5922 
5923  return $retStatusAry;
5924 } // end TxPostAdvanced
5925 
5926 /**
5927  * GetUserPhones
5928  * retrieve {cu}usercontact phone numbers for the
5929  * selected member
5930  *
5931  * @param string $CU current cu code
5932  * @param integer $Uid user id
5933  * @param array $HB_ENV home banking settings (uses dbh)
5934  * @return array ['mobile'] list of current phone numbers
5935  * empty array if no phone numbers found
5936  * array['errors'] if error occurs
5937  */
5938 function GetUserPhones($CU, $UID, $HB_ENV) {
5939 
5940  $dbh = $HB_ENV['dbh'];
5941  try {
5942  // get current list of phone numbers
5943  $aryPhones = array();
5944  $sql = "
5945  SELECT phones
5946  FROM {$CU}usercontact c
5947  LEFT JOIN {$CU}user u
5948  ON u.contact = c.contact_id
5949  WHERE u.user_id = {$UID}";
5950  $sqlRs = db_query($sql, $dbh);
5951  if (!$sqlRs) {
5952  throw new Exception("Error reading phone numbers");
5953  }
5954  $aryPhones = db_fetch_assoc($sqlRs, 0);
5955  if ($aryPhones['phones'] === null) {
5956  $aryPhones = array(
5957  "mobile" => array()
5958  );
5959  } else {
5960  $aryPhones = HCU_JsonDecode($aryPhones['phones']);
5961  }
5962 
5963 } catch (Exception $e) {
5964  $aryPhones['errors'] = $e->getMessage();
5965 }
5966 return $aryPhones;
5967 }
5968 function format_us_number($phone) {
5969  $numbers_only = preg_replace("/[^\d]/", "", $phone);
5970  return preg_replace("/^1?(\d{3})(\d{3})(\d{4})$/", "$1-$2-$3", $numbers_only);
5971 }
5972 //function slurpCurl($url, $postdata, $postcookie, $posthdrs) {
5973 // /* called with
5974 // $myTicket = appTokenCookie($HB_ENV);
5975 // $reqUrl = "http://localhost/banking/hcuExternalAccts.data?cu={$HB_ENV['cu']}";
5976 // $reqParams = json_encode(array('action' => "get_m2m_accounts"));
5977 // $result = slurpCurl($reqUrl, $reqParams, "Ticket=$myTicket",array());
5978 // $reply_arr = json_decode($result,true);
5979 // */
5980 // $curlopts = array(
5981 // CURLOPT_RETURNTRANSFER => 1,
5982 // CURLOPT_SSL_VERIFYPEER => 0,
5983 // CURLOPT_SSL_VERIFYHOST => 0,
5984 // CURLOPT_HEADER => 0,
5985 // CURLOPT_POST => 1,
5986 // CURLOPT_POSTFIELDS => "$postdata",
5987 // CURLOPT_URL => "$url");
5988 //
5989 // $ch = @curl_init();
5990 // @curl_setopt_array($ch, $curlopts);
5991 // if (!empty($postcookie)) {
5992 // curl_setopt($ch, CURLOPT_COOKIE, $postcookie);
5993 // }
5994 // if (is_array($posthdrs) && count($posthdrs)) {
5995 // curl_setopt($ch, CURLOPT_HTTPHEADER, $posthdrs);
5996 // }
5997 //curl_setopt($ch, CURLOPT_VERBOSE, true);
5998 //$verbose = fopen('/home/scrubcu/tmp/ajax_trace.txt', 'a+');
5999 //curl_setopt($ch, CURLOPT_STDERR, $verbose);
6000 //
6001 // $response = @curl_exec($ch);
6002 // $respHTTP = curl_getinfo($ch,CURLINFO_HTTP_CODE);
6003 // if ($respHTTP > 400 && $respHTTP < 600 ) {
6004 // # HTTP Response 4xx client error or 5xx server error
6005 // $response = "HCUERROR: Connection Failed HTTP Error $respHTTP";
6006 // } elseif (curl_errno($ch)) {
6007 // # Bad! Don't hide the error, return the curl error if it occurred.
6008 // $response = "HCUERROR: Connection Failed " . curl_error($ch);
6009 // }
6010 //
6011 // @curl_close($ch);
6012 // return $response;
6013 //}
6014 function appTokenCookie ($HB_ENV) {
6015  $now = time();
6016  $expires = $now + $HB_ENV['SYSENV']['ticket']['expires'];
6017 
6018  $mycookie = "Ctime=$now"
6019  . "&Ce=$expires"
6020  . "&Cu={$HB_ENV['Cu']}"
6021  . "&Cn={$HB_ENV['Cn']}"
6022  . "&Uid={$HB_ENV['Uid']}"
6023  . "&Clw={$HB_ENV['livewait']}"
6024  . "&Clu={$HB_ENV['lastupdate']}"
6025  . "&Fplog={$HB_ENV['Fplog']}"
6026  . "&Fflog={$HB_ENV['Fflog']}"
6027  . "&Ffchg={$HB_ENV['Ffchg']}"
6028  . "&Ffreset={$HB_ENV['Ffreset']}"
6029  . "&Ffremain={$HB_ENV['Ffremain']}"
6030  . "&Fmsg_tx={$HB_ENV['Fmsg_tx']}"
6031  . "&Fset={$HB_ENV['Fset']}"
6032  . "&Fset2={$HB_ENV['Fset2']}"
6033  . "&Fset3={$HB_ENV['Fset3']}"
6034  . "&Fhdays={$HB_ENV['histdays']}"
6035  . "&Ml=" . urlencode(trim($HB_ENV['Ml']))
6036  . "&Ca=";
6037  // ** Check for testmenu param -- add it to cookie if set
6038  if (intval(HCU_array_key_value('testmenu', $HB_ENV['HCUPOST'])) == 1) {
6039  $mycookie .= "&testmenu=1";
6040  }
6041  $returnTicket = SetTicket($HB_ENV,"", $mycookie);
6042 return $returnTicket;
6043 }
6044 
6045 /**
6046  * GatherUserActivity
6047  * Call the function to gather the requested user activity data. This does not
6048  * get the detail.
6049  *
6050  * @param array $pHBEnv - Home banking settings
6051  * @param array $pPass - The passed in parameters
6052  * @return array of information to return to app
6053  */
6054 function GatherUserActivity( $pHBEnv, $pPass ) {
6055  require_once(dirname(__FILE__) . '/../library/hcuUserActivity.i');
6056 
6057  // return the standard header block
6058  $replyArr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO') );
6059 
6060  // add some fields
6061  $replyArr["DTSERVER"] = date('YmdHis');
6062  $replyArr["MEMBER"] = $pHBEnv['Uid'];
6063 
6064  try {
6065  // make the parameter keynames consistent
6066  $params = array_change_key_case( $pPass, CASE_LOWER );
6067 
6068  // these needs to be here for other tests
6069  $params["detail"] = !isset( $params["detail"] ) ? 0 : intval($params["detail"]);
6070  $params["prior"] = !isset( $params["prior"] ) ? 0 : 1;
6071  $params["pending"] = !isset( $params["pending"] ) ? 0 : 1;
6072  // this can be "all" or true/1
6073  $params["scheduled"] = !isset( $params["scheduled"] ) ? "" : strtolower($params["scheduled"]);
6074 
6075  // make sure we have parameter for all possible variables
6076  $paramsToPassOn = array();
6077 
6078  // initialize anything we might be returning
6079  $resultMeta = null;
6080  $resultPending = null;
6081  $resultScheduled = null;
6082  $resultPrior = null;
6083 
6084  $paramsToPassOn["action"] = !isset( $params["action"] ) ? "" : $params["action"];
6085 
6086  // apps will never filter on type
6087  $paramsToPassOn["filter"]["type"] = "";
6088 
6089  // see if user wants the meta data (actions, featuers, statuses)
6090  if ( isset( $params["initial"] ) && $params["initial"] == 1 ) {
6091  $resultMeta = GetUserActivityMetaData( $pHBEnv );
6092 
6093  // check for errors
6094  if ( $resultMeta["code"] != "000" ) {
6095  throw new Exception( "User activity: " . $resultMeta["error"], 15552 );
6096  }
6097  }
6098 
6099  if ( $params["pending"] == 1 ) {
6100  $resultPending = GetUserActivity( $pHBEnv, "pending", $paramsToPassOn );
6101 
6102  // check for errors
6103  if ( $resultPending["code"] != "000" ) {
6104  throw new Exception( "User activity: " . $resultPending["error"], 15554 );
6105  }
6106  }
6107 
6108  if ( $params["scheduled"] == "all" ) {
6109  $paramsToPassOn["filter"]["scheduled"] = "all";
6110 
6111  $resultScheduled = GetUserActivity( $pHBEnv, "scheduled", $paramsToPassOn );
6112 
6113  // check for errors
6114  if ( $resultScheduled["code"] != "000" ) {
6115  throw new Exception( "User activity: " . $resultScheduled["error"], 15551 );
6116  }
6117  } else if ( $params["scheduled"] != "" ) {
6118  // initially, want just next 30 days of scheduled
6119  $initialScheduledFilter = array( "filter" => array( "start" => date( "Ymd" ),
6120  "end" => date( "Ymd", time() + 30 * 24 * 60 * 60 ) ) );
6121  $resultScheduled = GetUserActivity( $pHBEnv, "scheduled", $initialScheduledFilter );
6122 
6123  // check for errors
6124  if ( $resultScheduled["code"] != "000" ) {
6125  throw new Exception( "User activity: " . $resultScheduled["error"], 15555 );
6126  }
6127  }
6128 
6129  // see if prior data
6130  if ( $params["prior"] == 1 ) {
6131  // if missing dtend then use today
6132  if ( !isset( $params["dtend"] ) || ($params["dtend"] == "") ) {
6133  $dateEnd = date( "Y-m-d" );
6134  } else {
6135  $dateEnd = $params["dtend"];
6136  }
6137 
6138  // if missing dtstart then use 30 days ago
6139  if ( !isset( $params["dtstart"] ) || ($params["dtstart"] == "") ) {
6140  $dateStart = date( "Y-m-d", time() - 30 * 24 * 60 * 60 );
6141  } else {
6142  $dateStart = $params["dtstart"];
6143  }
6144 
6145  // test that start is before end
6146  $testStart = strtotime( $dateStart );
6147  $testEnd = strtotime( $dateEnd );
6148  if ( $testStart > $testEnd ) {
6149  throw new Exception( "User activity prior date start need to be <= date end", 15553 );
6150  }
6151 
6152  $paramsToPassOn["filter"]["start"] = $dateStart;
6153  $paramsToPassOn["filter"]["end"] = $dateEnd;
6154 
6155  $resultPrior = GetUserActivity( $pHBEnv, "prior", $paramsToPassOn );
6156 
6157  // check for errors
6158  if ( $resultPrior["code"] != "000" ) {
6159  throw new Exception( "User activity: " . $resultPrior["error"], 15556 );
6160  }
6161 
6162  // add to the reply the start and end date that was used
6163  $resultPrior["data"]["dtstart"] = date( "Y-m-d", strtotime( $dateStart ) );
6164  $resultPrior["data"]["dtend"] = date( "Y-m-d", strtotime( $dateEnd ) );
6165  }
6166 
6167  // return the data - everything that was collected
6168  $replyArr["ACTIVITY"] = array();
6169 
6170  if ( $resultMeta != null ) {
6171  $replyArr["ACTIVITY"]["meta"] = $resultMeta["data"];
6172  }
6173 
6174  // make sure data is ready to be returned
6175  if ( $resultPending != null && is_array($resultPending['data']) ) {
6176  array_walk_recursive( $resultPending['data'], 'MakeHTMLEntities' );
6177 
6178  $replyArr['ACTIVITY']["pending"] = $resultPending['data'];
6179  }
6180 
6181  if ( $resultScheduled != null && is_array($resultScheduled['data']) ) {
6182  array_walk_recursive( $resultScheduled['data'], 'MakeHTMLEntities' );
6183 
6184  $replyArr['ACTIVITY']["scheduled"] = $resultScheduled['data'];
6185  }
6186 
6187  if ( $resultPrior != null && is_array($resultPrior['data']) ) {
6188  array_walk_recursive( $resultPrior['data'], 'MakeHTMLEntities' );
6189 
6190  $replyArr['ACTIVITY']["prior"] = $resultPrior['data'];
6191  }
6192 
6193  // we are successful at this point
6194 
6195  } catch( Exception $e ) {
6196  $code = $e->getCode();
6197  $message = $e->getMessage();
6198 
6199  // return an error header block
6200  $replyArr["STATUS"] = array('CODE' =>$code,'SEVERITY' => 'ERROR');
6201  $replyArr["MESSAGE"] = array( "ERR" => $message );
6202  }
6203 
6204  return $replyArr;
6205 } // end GatherUserActivity
6206 
6207 /**
6208  * GatherUserActivityDetail
6209  * Call the function to gather the requested user activity data detail.
6210  *
6211  * @param array $pHBEnv - Home banking settings
6212  * @param int $pDetailId - The detail id to get
6213  * @param boolean $pIsScheduled - The type of txn getting detail for is a scheuled txn
6214  * @return array of information to return to app
6215  */
6216 function GatherUserActivityDetail( $pHBEnv, $pDetailId, $pIsScheduled ) {
6217  require_once(dirname(__FILE__) . '/../library/hcuUserActivity.i');
6218 
6219  // return the standard header block
6220  $replyArr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO') );
6221 
6222  // add some fields
6223  $replyArr["DTSERVER"] = date('YmdHis');
6224  $replyArr["MEMBER"] = $pHBEnv['Uid'];
6225 
6226  try {
6227  $resultDetail = GetActivityDetail( $pHBEnv, $pDetailId, $pIsScheduled );
6228 
6229  // return the data - everything that was collected
6230  $replyArr["ACTIVITY"] = array();
6231 
6232  if ( $resultDetail != null && is_array($resultDetail['data']) ) {
6233  array_walk_recursive( $resultDetail['data'], 'MakeHTMLEntities' );
6234  }
6235 
6236 
6237  $replyArr['ACTIVITY']["details"] = $resultDetail['data'];
6238 
6239  // we are successful at this point
6240 
6241  } catch( Exception $e ) {
6242  $code = $e->getCode();
6243  $message = $e->getMessage();
6244 
6245  // return an error header block
6246  $replyArr["STATUS"] = array('CODE' =>$code,'SEVERITY' => 'ERROR');
6247  $replyArr["MESSAGE"] = array( "ERR" => $message );
6248  }
6249 
6250  return $replyArr;
6251 } // end GatherUserActivityDetail
6252 
6253 /**
6254  * UserActivityAction
6255  * Call the function to gather the requested user activity data detail.
6256  *
6257  * @param array $pHBEnv - Home banking settings
6258  * @param int $pDetailId - The detail id to act upon
6259  * @param boolean $pIsScheduled - The type of txn acting upon is a scheuled txn
6260  * @param string $pAction - The specific action
6261  * @return array of information to return to app
6262  */
6263 function UserActivityAction( $pHBEnv, $pDetailId, $pIsScheduled, $pAction ) {
6264  require_once(dirname(__FILE__) . '/../library/hcuUserActivity.i');
6265 
6266  // return the standard header block
6267  $replyArr = array( 'STATUS' => array('CODE' => 0,'SEVERITY' => 'INFO') );
6268 
6269  // add some fields
6270  $replyArr["DTSERVER"] = date('YmdHis');
6271  $replyArr["MEMBER"] = $pHBEnv['Uid'];
6272 
6273  try {
6274  // this call will return result detail
6275  $resultDetail = DoActivityAction( $pHBEnv, $pDetailId, $pIsScheduled, $pAction );
6276 
6277  if ( $resultDetail["code"] != "000" ) {
6278  // pass along the error
6279  throw new Exception($resultDetail["error"]);
6280  }
6281 
6282  // add a message to the reply (the action would have been successful to get to this point)
6283  $message = "";
6284  if ( $pAction == "approve" ) {
6285  if ( isset( $resultDetail["data"]["confirm"] ) && strlen( $resultDetail["data"]["confirm"] ) > 0 ) {
6286  $message = $pHBEnv["MC"]->msg("Transaction Sent", HCU_DISPLAY_AS_RAW) .
6287  "\n\n" .
6288  $pHBEnv["MC"]->msg("Confirmation", HCU_DISPLAY_AS_RAW) . ": " . $resultDetail["data"]["confirm"];
6289  } else {
6290  $message = $pHBEnv["MC"]->msg("record approved", HCU_DISPLAY_AS_RAW);
6291  }
6292  } else if ( $pAction == "decline" ) {
6293  $message = $pHBEnv["MC"]->msg("record declined", HCU_DISPLAY_AS_RAW);
6294  } else if ( $pAction == "cancel" ) {
6295  $message = $pHBEnv["MC"]->msg("record cancelled", HCU_DISPLAY_AS_RAW);
6296  } else {
6297  $message = "Unknown action received: $pAction";
6298  }
6299 
6300  $resultDetail["data"]["message"] = $message;
6301 
6302  // return the data - everything that was collected
6303  $replyArr["ACTIVITY"] = array();
6304 
6305  if ( $resultDetail != null && is_array($resultDetail['data']) ) {
6306  array_walk_recursive( $resultDetail['data'], 'MakeHTMLEntities' );
6307  }
6308 
6309  $replyArr['ACTIVITY'] = $resultDetail['data'];
6310 
6311  // we are successful at this point
6312 
6313  } catch( Exception $e ) {
6314  $code = $e->getCode();
6315  $message = $e->getMessage();
6316 
6317  // return an error header block
6318  $code = $code > 0 ? $code : "999";
6319  $replyArr["STATUS"] = array('CODE' =>$code,'SEVERITY' => 'ERROR');
6320  $replyArr["MESSAGE"] = array( "ERR" => $message );
6321  }
6322 
6323  return $replyArr;
6324 }
6325 
6326 // Get any pending user activity and return the count of what this user can approve.
6327 // This is copied from hcuPreContent.i.
6328 function GetPendingUserActivity( $pHBEnv ) {
6329  $ucTransactionsCount = 0;
6330 
6331  $ucTransactions = ReturnUnconfirmedTransactions($pHBEnv['dbh'], $pHBEnv);
6332  if (is_array($ucTransactions)) {
6333  foreach ($ucTransactions as $key => $value) {
6334  $canConfirm = Perm_AccessRights($pHBEnv['dbh'], $pHBEnv, array("feature" => $value['feature_code']));
6335  if ($canConfirm['confirm']) {
6336  $ucTransactionsCount ++;
6337  }
6338  }
6339  }
6340 
6341  return $ucTransactionsCount;
6342 } // end GetPendingUserActivity
6343 
6344 // Temporary until the actual function is developed.
6345 function GetFakeUserCompassMenu( $pHBEnv ) {
6346  $fakeData = <<< ENDOFFAKEDATA
6347 { "logout":{
6348  "display":"Sign Out",
6349  "icon":"power-off",
6350  "action":"exit"
6351  },
6352  "activity":{
6353  "display":"User Activity",
6354  "icon":"bell",
6355  "count":"2",
6356  "menuid":"33",
6357  "script":"hcuUserActivity.prg",
6358  "action":"menu"
6359  },
6360  "messages":{
6361  "display":"Secure Messages",
6362  "icon":"comments",
6363  "count":"0",
6364  "menuid":"47",
6365  "script":"hcuSecureMail.prg",
6366  "action":"menu"
6367  },
6368  "status": {
6369  "display":"Status",
6370  "icon":"tasks",
6371  "count":"1",
6372  "endpoint":"http://10.100.10.76:8000/banking/hcuAccountStatus.prg?cu=SCRUBCU",
6373  "action":"popup"
6374  },
6375  "user": {
6376  "display":"User Info",
6377  "icon":"user",
6378  "count":"2",
6379  "action":"popup",
6380  "password": {
6381  "title": "Password update required!",
6382  "message": "Login expires after 20 more uses.",
6383  "icon": "exclamation",
6384  "level": "warning",
6385  "link": "Update Now",
6386  "script":"hcuProfilePwd.prg",
6387  "menuid":"52",
6388  "action":"menu"
6389  },
6390  "email": {
6391  "title": "Your current email",
6392  "message": "person@example.com",
6393  "icon": "envelope",
6394  "level": "info",
6395  "link": "Update Now",
6396  "script":"hcuProfileEmail.prg",
6397  "menuid":"53",
6398  "action":"menu"
6399  },
6400  "security": {
6401  "title": "Security credentials update required!",
6402  "message": "Login expires after 1 more use.",
6403  "icon": "exclamation-triangle",
6404  "level": "error",
6405  "link": "Update Now",
6406  "script":"hcuProfileSecurity.prg",
6407  "menuid":"54",
6408  "action":"menu"
6409  }
6410  }
6411 }
6412 ENDOFFAKEDATA;
6413 
6414  return $fakeData;
6415 }
6416 
6417 // This is a callback function used to make htmlentities from strings in arrays to be returned to the
6418 // caller. It is used when XML or JSON will be returned.
6419 function MakeHTMLEntities( &$item, $key ) {
6420  $item = htmlentities( $item, ENT_NOQUOTES | ENT_XML1, 'UTF-8', FALSE );
6421  }
static GetInstance($dbh)