<?php
require_once('config.php');require_once('include/service/contacts_match.php');
$uid=$_SESSION['user'];
$userFile=ABSPATH.'data/users/'.preg_replace('/[^a-zA-Z0-9_-]/','',$uid).'.meta';
$uData=file_exists($userFile)?loadMeta($userFile):[];
$uPrefs=$uData['prefs']??[];
$PER_PAGE_DESKTOP=(int)($uPrefs['PER_PAGE_DESKTOP']??$PER_PAGE_DESKTOP);
$PER_PAGE_MOBILE=(int)($uPrefs['PER_PAGE_MOBILE']??$PER_PAGE_MOBILE);
$inboxsortMode=$uPrefs['inboxsortMode']??$inboxsortMode;
$inboxfoldersortMode=$uPrefs['inboxfoldersortMode']??$inboxfoldersortMode;
$inboxfilesortMode=$uPrefs['inboxfilesortMode']??$inboxfilesortMode;
if(!isset($_SESSION['user'])||!has_permission('inboxmenu')){header("Location:login.php");exit;}
function faxViewToken(string $path):string{$data=realpath($path);$ts=time();$payload=$ts.'|'.$data;$sig=hash_hmac('sha256',$payload,FAX_VIEW_SECRET);return rtrim(strtr(base64_encode($sig.'|'.$payload),'+/','-_'),'=');}
$contactMap=getContactMap();
$msg=$_GET['msg']??'';
$baseArchivePath=realpath($applicationArchivePath);
$requestedDir=$_GET['dir']??'';
if(str_contains($requestedDir,'..'))exit('Invalid path');
$targetPath=$baseArchivePath.DIRECTORY_SEPARATOR.$requestedDir;
$fullPath=realpath($targetPath);
if($fullPath&&strpos($fullPath,$baseArchivePath)===0){
$dir=ltrim(str_replace($baseArchivePath,'',$fullPath),DIRECTORY_SEPARATOR);
}else{$dir='';$fullPath=$baseArchivePath;}
$page=max(1,(int)($_GET['page']??1));
$search=trim($_GET['q']??'');
$allDirs=isset($_GET['all_dirs'])&&$_GET['all_dirs']==='1';
$withMemo=isset($_GET['with_memo'])&&$_GET['with_memo']==='1';
$sortMode=$inboxsortMode;$foldersortMode=$inboxfoldersortMode;$filesortMode=$inboxfilesortMode;
$isMobile=preg_match('/Mobile|Android|iPhone|iPad/i',$_SERVER['HTTP_USER_AGENT']);
$perPage=$isMobile?$PER_PAGE_MOBILE:$PER_PAGE_DESKTOP;
function getInboxItems($currentPath,$search,$allDirs,$withMemo,$basePath,$depth=0){
if($depth>10)return[];
global$contactMap;
$items=[];
if(!is_dir($currentPath))return $items;
$realCurrent=realpath($currentPath);
$realBase=realpath($basePath);
if(!$realCurrent||strpos($realCurrent,$realBase)!==0)return[];
$files=array_diff(scandir($currentPath),['.','..']);
foreach($files as $name){
$full=$currentPath.'/'.$name;
if(is_dir($full)){
if(!$allDirs){
if($search===''||stripos($name,$search)!==false){$items[]=['name'=>$name,'type'=>'folder','mtime'=>filemtime($full),'relDir'=>''];}
}else{
$items=array_merge($items,getInboxItems($full,$search,$allDirs,$withMemo,$basePath,$depth+1));}
}elseif(strtolower(pathinfo($name,PATHINFO_EXTENSION))==='pdf'){
$metaFile=dirname($full).DIRECTORY_SEPARATOR.pathinfo($full,PATHINFO_FILENAME).'.meta';
$meta=loadMeta($metaFile);
$isRead=($meta['read']??false)===true;
$memo=trim($meta['memo']??'');
$hasMemo=($memo!=='');
$recvTime=$meta['received_time']??'';
$caller=$meta['caller']??'';
if(!$caller&&preg_match('/^\d{8}_\d{4}_(\d+)/',$name,$m))$caller=$m[1];
$status=$meta['status']??'unknown';
$contactName=$caller?resolveContactName($caller,$contactMap):'';
if($search!==''){
$match=false;
if(stripos($name,$search)!==false)$match=true;
if(!$match&&$caller&&stripos($caller,$search)!==false)$match=true;
if(!$match&&$contactName&&stripos($contactName,$search)!==false)$match=true;
if(!$match&&$withMemo&&$hasMemo&&stripos($memo,$search)!==false)$match=true;
if(!$match)continue;
}
$relDir=ltrim(str_replace($basePath,'',$currentPath),'/');
$items[]=[
'name'=>$name,
'type'=>'file',
'mtime'=>filemtime($full),
'isRead'=>$isRead,
'hasMemo'=>$hasMemo,
'memo'=>$memo,
'recvTime'=>$recvTime,
'caller'=>$caller,
'status'=>$status,
'relDir'=>$relDir
];
}}
return $items;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><?=ns(APP_NAME)?> - <?php _nsl('Fax Inbox');?></title>
<link rel="stylesheet" href="include/css/style.css">
<?php include('include/web/header.php');?>
<script>
window.FAX_LANG={
cancel:"<?=ns(__('Cancel'))?>",
delete:"<?=ns(__('Delete'))?>",
ok:"<?=ns(__('Ok'))?>",
searchFax:"<?=ns(__('Search Fax'))?>",
search:"<?=ns(__('Search'))?>",
keyword:"<?=ns(__('Enter keyword'))?>",
allDirs:"<?=ns(__('Search all folders'))?>",
withMemo:"<?=ns(__('Include memo content'))?>",
newFolder:"<?=ns(__('Create New Folder'))?>",
create:"<?=ns(__('Create'))?>",
folderAlert:"<?=ns(__('Enter new folder name'))?>",
deleteFax:"<?=ns(__('Delete Fax'))?>",
deleteFolder:"<?=ns(__('Delete Folder'))?>",
faxMemo:"<?=ns(__('Fax Memo'))?>",
alert:"<?=ns(__('Alert'))?>",
pushTitle:"<?=ns(__('Push Notification'))?>",
pushDesc:"<?=ns(__('You can register or test push notifications'))?>",
pushRegister:"<?=ns(__('Subscribe'))?>",
pushTest:"<?=ns(__('Test'))?>",
pushNotSupported:"<?=ns(__('Push is not supported in this browser'))?>",
pushNeedPermission:"<?=ns(__('Notification permission is required'))?>",
pushAlready:"<?=ns(__('Already subscribed'))?>",
pushSuccess:"<?=ns(__('Push subscription completed'))?>",
pushTestSuccess:"<?=ns(__('Test notification sent'))?>",
pushTestFail:"<?=ns(__('Test failed'))?>"
};
window.FAX_ENV={
dir:"<?=ns($dir)?>",
search:"<?=ns($search)?>",
allDirs:<?=$allDirs?1:0?>,
withMemo:<?=$withMemo?1:0?>,
csrf:"<?=ns($_SESSION['csrf']??'')?>",
inboxEdit:"fax_inbox_edit.php",
outboxEdit:"fax_outbox_edit.php",
isInbox:<?=basename($_SERVER['PHP_SELF'])==='fax_inbox.php'?'true':'false'?>
};
window.PUSH_CONFIG={publicKey:"<?=PUSH_PUBLIC_KEY?>"};
</script>
<script src="include/js/modal.js" defer></script>
<script src="include/js/base.js" defer></script>
</head>
<body>
<div id="menuToggle">≡ <?php _nsl('Menu');?></div>
<div id="container">
<?php include('menu.php');?>
<div id="main">
<h2><?php _nsl('Fax Inbox');?></h2>
<input type="hidden" id="csrf" value="<?=$_SESSION['csrf']?>">
<div class="table-wrapper">
<table>
<?php
$basePath=realpath(rtrim($applicationArchivePath,'/'));
$target=$basePath.($dir?'/'.$dir:'');
$path=realpath($target);
if(!$path||strpos($path,$basePath)!==0){$path=$basePath;$dir='';}
echo"<thead><tr><th class='faxlist-top' colspan='4'><div class='faxlist-header'><div class='faxlist-path'><a class='menulink' href='fax_inbox.php'><strong>".ns(__('Fax List'))."</strong></a>";
if(!$allDirs&&$dir!==''){
$subs=explode('/',$dir);
foreach($subs as $sd){
$linkPath=implode('/',array_slice($subs,0,array_search($sd,$subs)+1));
echo"&nbsp;&gt;&nbsp;<a class='menulink' href='fax_inbox.php?dir=".urlencode($linkPath)."'><strong>".ns($sd)."</strong></a>";
}}
echo"</div><div class='faxlist-actions'>";
if($dir===''&&has_permission('inboxaddfolder'))echo"<button class='faxlist-btn' onclick='openNewFolderDialog()'>".ns(__('New Folder'))."</button>";
if(has_permission('inboxsearch'))echo"<button class='faxlist-btn' onclick='openSearchDialog()'>".ns(__('Search'))."</button>";
echo"</div></div></th></tr></thead><tbody>";
$scanPath=$allDirs?$basePath:$path;
$items=getInboxItems($scanPath,$search,$allDirs,$withMemo,$basePath);
$folders=array_filter($items,fn($i)=>$i['type']==='folder');
$files=array_filter($items,fn($i)=>$i['type']==='file');
usort($folders,fn($a,$b)=>($foldersortMode==='on'?-1:1)*strnatcasecmp($a['name'],$b['name']));
usort($files,fn($a,$b)=>($filesortMode==='on'?-1:1)*strnatcasecmp($a['name'],$b['name']));
$fileList=($sortMode==='file')?array_merge($files,$folders):array_merge($folders,$files);
$totalItems=count($fileList);$totalPages=max(1,ceil($totalItems/$perPage));
$page=min($page,$totalPages);$slice=array_slice($fileList,($page-1)*$perPage,$perPage);
if(!$slice){echo"<tr><td colspan='4' class='web-msg'>".ns($search!==''?__('No fax found matching your search'):__('The FAX Inbox is empty'))."</td></tr>";
}
$i=0;
foreach($slice as $item){
$row=($i++%2)?'row-classic':'row-alternate';$name=$item['name'];$itemDir=$allDirs?$item['relDir']:$dir;
if($item['type']==='folder'){
$link="fax_inbox.php?dir=".urlencode($name);
$label="<a class='menulink' href='{$link}'><strong>".ns($name)."</strong></a>";
$iconWrapper="<div class='icon-wrapper'><a href='{$link}'><img src='img/box_folder.png'></a></div>";$cls='';
}else{
$real=realpath($basePath.($itemDir?'/'.$itemDir:'').'/'.$name);if(!$real)continue;
$num=$item['caller']?:'unknown';
$date='';
if(!empty($item['recvTime'])){$ts=strtotime($item['recvTime']);
if($ts){$date=date('Y/m/d H:i',$ts);}}
if(!$date&&preg_match('/^(\d{8}_\d{4})/',$name,$m)){$dt=DateTime::createFromFormat('Ymd_Hi',$m[1]);
$date=$dt?$dt->format('Y/m/d H:i'):$m[1];}
$status=strtolower($item['status']??'unknown');
$displayName=resolveContactName($num,$contactMap);
$cls=$item['isRead']?'fax-read':'fax-unread';
$newBadge=$item['isRead']?'':"<span class='fax-new'>N</span>";
$vLink='include/service/fax_view.php?id='.faxViewToken($real);
$memoIcon=$item['hasMemo']?" <span class='memo-icon' data-memo='".ns($item['memo'])."' data-date='".ns($date)."' onclick='openMemoAlert(this);event.preventDefault();event.stopPropagation();'><span class='fax-memo'>M</span></span>":'';
$iconWrapper="<div class='icon-wrapper'><img src='img/box_file.png' class='fax-icon'>{$newBadge}</div>";
$label="<a href='javascript:void(0);' onclick=\"openFaxViewer('".ns($vLink)."')\" class='menulink fax-link ".ns($cls)."'><strong>".$displayName."</strong></a>{$memoIcon}";
$label.="<br><span class='fax-date'>".ns($date)."</span>";}
echo"<tr class='{$row}'>
<td class='command_button'>
<a href='javascript:void(0);' onclick=\"openFaxViewer('".ns($vLink)."')\" class='fax-link ".ns($cls)."'>{$iconWrapper}</a></td>
<td>{$label}</td>";
$u=rawurlencode($item['type']==='folder'?$name:pathinfo($name,PATHINFO_FILENAME));
$editLink="fax_inbox_edit.php?fax=$u&dir=".urlencode($itemDir);
echo"<td class='icon'><a href='".ns($editLink)."'><img src='img/box_submenu.png'></a></td>";
if(has_permission('inboxdelete')){
$confirmMsg=($item['type']==='folder')
?__('The selected folder will be deleted. This action cannot be undone. Are you sure you want to proceed?')
:__('The selected fax will be deleted. This action cannot be undone. Are you sure you want to proceed?');
$deleteUrl=($item['type']==='folder')
?"include/service/remove_folder_inbox.php?folder=".$u
:"include/service/remove_file_inbox.php?fax=".$u."&dir=".urlencode($itemDir);
echo"<td class='icon'><a href='#' onclick=\"openDeleteDialog('".ns($deleteUrl)."','".ns($confirmMsg)."','".ns($item['type'])."');return false;\"><img src='img/box_remove.png'></a></td>";}
echo"</tr>";
}
if($totalPages>1){
$q='dir='.urlencode($dir).'&q='.urlencode($search).'&all_dirs='.($allDirs?1:0).'&with_memo='.($withMemo?1:0);
echo"<tr><th colspan='4'><div class='pagination'>";
if($page>1)echo"<input type='button' class='page-btn' value='«' onclick=\"location.href='?$q&page=1'\">";
if($page>1)echo"<input type='button' class='page-btn' value='‹' onclick=\"location.href='?$q&page=".($page-1)."'\">";
for($p=max(1,$page-2);$p<=min($totalPages,$page+2);$p++){
$c=($p==$page)?'page-btn current':'page-btn';
echo"<input type='button' class='$c' value='$p' onclick=\"location.href='?$q&page=$p'\">";
}
if($page<$totalPages)echo"<input type='button' class='page-btn' value='›' onclick=\"location.href='?$q&page=".($page+1)."'\">";
if($page<$totalPages)echo"<input type='button' class='page-btn' value='»' onclick=\"location.href='?$q&page=$totalPages'\">";
echo"</div></th></tr>";
}
?>
</tbody></table>
</div></div></div>
<?php include('include/web/modal.php');?>
<?php include('include/web/footer.php');?>
</body>
</html>
