<?

	// Duplicate check
		if ( defined("_INCLUDED_FUNC") ) return;
		define("_INCLUDED_FUNC", "1");


// [2010-09-15] 편지함 백업시 파일명 변경 지원.
function get_mail_box_backup_filename($mf_arrival_date, $mf_subject, $mf_from, $mf_to, $mb_id)
{
	$filename = '';

	// 받은날짜
	$filename .= date('Ymd_Hi', strtotime($mf_arrival_date));
	$filename .= '_';

	// 보낸이름/받는이름
	if (in_array($mb_id, array('sent', 'draft')))
	{
		$address = $mf_to;
	} else
	{
		$address = $mf_from;
	}	// if()
	$_ap = mime_address_parse($address);
	$filename .= $_ap['display'];
	$filename .= '_';

	// 제목
	$filename .= $mf_subject;

	// 파일명 길이는 100자로 제한하고, 파일명으로 사용불가한 문자는 제거함.
	$filename = han_substr(file_name_char_limit($filename), 100);

	return $filename;
}	// function()


// [2009-03-27] White IP 에서는 RBL 체크 생략
function netmask2bitmask($ipaddr, $netmask)
{
	$bitmask = strlen(str_replace('0', '', decbin(ip2long($netmask))));
	return $ipaddr.'/'.$bitmask;
}

function bitmask2netmask($ipaddr)
{
	$_ex = explode('/', trim($ipaddr));
	$_ex[1] = long2ip(0xffffffff << (32 - $_ex[1]));
	return $_ex;
}


// [2008-10-10] 다중 언어팩
function lang_conv($str)
{
	global $G_SYS, $NLANG;
	if ($G_SYS['MULTI_LANG_SUPPORT'] && $G_SYS['nm_lang'] != 'ko')
	{
		$_as = array_search(trim($str), $NLANG['ko']);
		if ($_as !== false)
			$str = $NLANG[$G_SYS['nm_lang']][$_as];
	}	// if()

	return $str;
}	// function()


// [2008-08-22] 주소록 그룹이 좌측에 보여지는 스킨일때
function nm_iframe_sync($frame_name, $height=true, $width=false)
{
	?>
		<script type="text/javascript">
			<!--
				function nm_iframe_sync(obj)
				{
					try
					{
						<? if ($height) { ?>
							if (parent.document.getElementById(obj).height < document.body.scrollHeight || (parseInt(parent.document.getElementById(obj).height, 10) >= 500 && parent.document.getElementById(obj).height > document.body.scrollHeight))
								parent.document.getElementById(obj).height = document.body.scrollHeight;
						<? }	// if() ?>

						<? if ($height) { ?>
							if (parent.document.getElementById(obj).width < document.body.scrollWidth)
								parent.document.getElementById(obj).width = document.body.scrollWidth;
						<? }	// if() ?>
					}
					catch (e) {}
				}	// function()

				nm_iframe_sync('<?= $frame_name ?>');

				if(typeof window.addEventListener != 'undefined')
				{
					window.addEventListener("load", function() { nm_iframe_sync('<?= $frame_name ?>'); }, false);
				} else if(typeof window.attachEvent != 'undefined')
				{
					window.attachEvent( "onload", function () { nm_iframe_sync('<?= $frame_name ?>'); } );
				}
		   //-->
		</script>
	<?
}	// function()

function nm_iframe_load($frame_name, $title_name='', $title='')
{
	nm_iframe_sync($frame_name, 1, 1);

	if ($title_name != '' && $title != '')
	{
		?>
			<script type="text/javascript">
				<!--
					try
					{
						parent.document.getElementById('<?= $title_name ?>').innerHTML = '<?= $title ?>';
					}
					catch (e) {}
			   //-->
			</script>
		<?
	}	// if()
}	// function()


// [2008-02-21] 에러메세지를 ActiveX 로 전달
function errorview($msg, $code)
{
	global $G_SYS;

	@header("HTTP/1.1 500");

	/*
	if ($G_SYS['NM_UPLOADER_CHARSET'] != 'UTF-8' && strtoupper($G_SYS[CHARSET]) == 'UTF-8')
	{
		echo str_replace("\\n", "\n", iconv_fix('UTF-8', $msg, 'EUC-KR'));
	} else
	{
		echo str_replace("\\n", "\n", $msg);
	}	// if()
	*/
	if (strtoupper($G_SYS[CHARSET]) != 'UTF-8')
		echo str_replace("\\n", "\n", iconv_fix($G_SYS[CHARSET], $msg, 'UTF-8'));
	else
		echo str_replace("\\n", "\n", $msg);

	error($msg, $code, $isexit=true, $isback=false, $isview=false);
}	// function()


// [2008-02-19] 웹하드 용량
function get_webhard_quota($m_no)
{
	global $G_SYS, $TB;

	if ($m_no > 0)
	{
		if (!isset($_SESSION["auth_m_webhard_quota"]))
		{
			$q = "SELECT m_webhard_quota, m_bigfile_quota FROM ".$TB['MEMBER']." WHERE m_no='".$m_no."'";
			$row = db_get_row($q);
			$_SESSION["auth_m_webhard_quota"] = $row['m_webhard_quota'];
			$_SESSION["auth_m_bigfile_quota"] = $row['m_bigfile_quota'];
		}	// if()

		return $_SESSION["auth_m_webhard_quota"];
	}	// if()
}	// function()


// [2008-02-19] 대용량첨부 용량
function get_bigfile_quota($m_no)
{
	global $G_SYS, $TB;

	if ($m_no > 0)
	{
		if (!isset($_SESSION["auth_m_bigfile_quota"]))
		{
			$q = "SELECT m_webhard_quota, m_bigfile_quota FROM ".$TB['MEMBER']." WHERE m_no='".$m_no."'";
			$row = db_get_row($q);
			$_SESSION["auth_m_webhard_quota"] = $row['m_webhard_quota'];
			$_SESSION["auth_m_bigfile_quota"] = $row['m_bigfile_quota'];
		}	// if()

		return $_SESSION["auth_m_bigfile_quota"];
	}	// if()
}	// function()


// [2008-02-15] ActiveX 사용여부
function get_activex_use()
{
	global $G_SYS;

	if (!isset($G_SYS['NM_ACTIVEX_USE']))
	{
		// [2013-10-28] IE 11 fix
		//if (strpos($_SERVER['HTTP_USER_AGENT'],"MSIE") !== false)
		if (strpos($_SERVER['HTTP_USER_AGENT'],"MSIE") !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false)
		{
			if ($_COOKIE['nm_upmode'] == 'T')
			{
				$G_SYS['NM_ACTIVEX_USE'] = false;
			} else if (is_multi_upload())
			{
				$G_SYS['NM_ACTIVEX_USE'] = false;
			} else
			{
				$G_SYS['NM_ACTIVEX_USE'] = true;
				// [2012-04-30] 모바일웹
				if ($_COOKIE['nm_mobile'])
					$G_SYS['NM_ACTIVEX_USE'] = false;
			}	// if()
		} else
		{
			$G_SYS['NM_ACTIVEX_USE'] = false;
		}	// if()
	}	// if()

	return $G_SYS['NM_ACTIVEX_USE'];
}	// function()

/**
 * 멀티업로드 사용여부
 *   - 22.11.14 쿠키없을 경우 기본 로딩을 멀티업로드로 변경.
 *
 * @return bool
 */
function is_multi_upload()
{
    return ( ! isset($_COOKIE['nm_upmode']) || $_COOKIE['nm_upmode'] === 'M');
}

/**
 * 웹에디터 기본 로딩 여부
 *  - 23.10.05 모바일은 TINYMCE5 에서만 웹에디터 기본 로딩 허용
 *
 * @return false|int
 */
function is_not_default_webeditor()
{
    global $G_SYS;

	if ($G_SYS['WEBEDITOR'] == 'TINYMCE5')
    {
        return false;
    }

	return preg_match('/iPhone|iPad|Android|Mobile Safari|BlackBerry|SymbianOS|Windows CE|Opera Mini|ARM/', $_SERVER['HTTP_USER_AGENT']);
}	// function()


// [2008-01-30] 키값 배열을 검색이 쉬운 문자열로 변환.  ex)  "|16|17|18|15|"
function db_str_implode($sp, $ar)
{
	if (is_array($ar) && sizeof($ar) > 0)
	{
		// [2026-07-10] KVE-2026-1557 아이디, 그룹번호 이외 특수문자 제한
		foreach ($ar as $key => $val) {
			if (! preg_match('/^[a-zA-Z0-9\-_.]+$/', $val)) {
				security_log('비정상 접근이 차단되었습니다.', 'db_str_implode()', $val);
			}
		}

		$return = $sp.implode($sp, $ar).$sp;
	} else
	{
		$return = "";
	}	// if()
	return $return;
}	// function()

// [2008-01-30] 검색이 쉬운 문자열로 키값 배열로 복원.
function db_str_explode($sp, $str)
{
	$return = array();
	if (strpos($str, $sp) !== false)
	{
		$ex = explode($sp, $str);
		for ($i=0; $i<sizeof($ex); $i++)
		{
			if (trim($ex[$i]) != '')
			{
				$return[] = $ex[$i];
			}	// if()

		}	// for()
	} else if (trim($str) != '')
	{
		$return[] = $str;
	}	// if()

	// [2026-07-10] KVE-2026-1557 아이디, 그룹번호 이외 특수문자 제한
	foreach ($return as $key => $val) {
		if (! preg_match('/^[a-zA-Z0-9\-_.]+$/', $val)) {
			security_log('비정상 접근이 차단되었습니다.', 'db_str_explode()', $val);
		}
	}

	return $return;
}	// function()


// [2007-01-18] HTTP_HOST 에서 포트를 제외한 도메인만 가져오기
function get_http_host_domain($host)
{
	$__http_host = $host;
	if (strpos($host, ':') !== false)
	{
		$__url = parse_url($host);
		$__http_host = $__url['host'];
	}	// if()
	return $__http_host;
}	// function()


// [2006-07-25] 기본 설정 가져오기
function get_default_config($m_no)
{
	global $TB, $AUTH, $G_SYS;
	// [2010-10-06] 보낸주소 선택
	if ($G_SYS['NM_MAIL_FROM_SEL_ISUSE'])
	{
		$row = db_get_row("SELECT m_cfg_new_mail_box, m_cfg_new_cnt, m_cfg_week_diray_cnt, m_cfg_from_name, m_cfg_from_addr, m_cfg_mail_list_cnt, m_send_preview, m_mail_box_backup_type FROM ".$TB['MEMBER']." WHERE m_no='".$m_no."'");
	} else
	{
		$row = db_get_row("SELECT m_cfg_new_mail_box, m_cfg_new_cnt, m_cfg_week_diray_cnt, m_cfg_from_name, m_cfg_mail_list_cnt, m_send_preview, m_mail_box_backup_type FROM ".$TB['MEMBER']." WHERE m_no='".$m_no."'");
	}	// if()
	if (!$row)
	{
		$row = $G_SYS['DEFAULT_CONFIG_DEFAULT'];
	} else
	{
		foreach ($G_SYS['DEFAULT_CONFIG_DEFAULT'] as $key=>$val)
		{
			if ($key == 'm_cfg_from_name' && $row[$key] == '')
			{
				$row[$key] = $AUTH['auth_m_name'];
			}	// if()
			if (!$row[$key])
			{
				$row[$key] = $val;
			}	// if()
		}	// foreach()
	}	// if()

	return $row;
}	// function()


// [2006-07-04] 현재 메뉴에서 '환경설정, 관리자메뉴'의 경우 첫번째 메뉴에 자동링크를 걸어줌.
function title_autolink($title, $islink=true)
{
	global $nmpath, $G_SYS;

	if ($G_SYS['TITLE_AUTOLINK_SKIP'])
		return $title;

	$thispage = basename($_SERVER['PHP_SELF']);
	$viewmenu = '';
	#if (!in_array($thispage, array('board.php', 'mail_list.php', 'mail_read.php', 'mail_header_view.php')))
	preg_match_all('/\<b>([^<]+)\<\/b>/', $title, $mc);
	$cnt = sizeof($mc[1]);

	if ($cnt >= 2)
	{
		for ($i=0; $i<$cnt; $i++)
		{
			switch ($mc[1][$i])
			{
				case '주소록':
					$mc[2][$i] = $nmpath.'/addr_list.php';
					break;

				case '일정관리':
					$mc[2][$i] = $nmpath.'/diary_month.php';
					break;

				case '환경설정':
					$mc[2][$i] = $nmpath.'/mail_config.php';
					break;

				case '관리자메뉴':
					$mc[2][$i] = $nmpath.'/admin.php';
					break;

				case '메일서버관리':
					$mc[2][$i] = $nmpath.'/sadmin.php';
					break;

				case '공용게시판관리':
					$mc[2][$i] = $nmpath.'/board.php?mode=ADMIN_LIST';
					break;
			}	// switch()

			if ($viewmenu != '')
				$viewmenu .= ' >> ';

			if ($i < $cnt-1 && $mc[2][$i] != '')
			{
				if ($islink)
				{
					$viewmenu .= '<a href="'.lang_conv($mc[2][$i]).'"><b><u>'.lang_conv($mc[1][$i]).'</u></b></a>';
				} else
				{
					$viewmenu .= '<b>'.lang_conv($mc[1][$i]).'</b>';
				}	// if()
			} else
			{
				$viewmenu .= '<b>'.lang_conv($mc[1][$i]).'</b>';
			}	// if()
		}	// for()
	} else if ($mc[1][0] != '')
	{
		$viewmenu = str_replace($mc[1][0], lang_conv($mc[1][0]), $mc[0][0]);
	}	// if()

	if ($viewmenu == '')
		$viewmenu = $title;

	return $viewmenu;
}	// function()

// 도메인별 스킨 적용.
function set_nskin()
{
	global $NSKIN, $NLANG, $G_SYS, $G, $G_BOARD;

	// [2006-07-07] global.php 에서 미리 가져옴.
	//$G['DINFO'] = get_domain($G_SYS['MAIL_DOMAIN']);
	$NSKIN['main'] = $G['DINFO']['d_skin_main'];
	$NSKIN['webmail'] = $G['DINFO']['d_skin_webmail'];
	$NSKIN['member'] = $G['DINFO']['d_skin_member'];
	$NSKIN['board'] = $G['DINFO']['d_skin_board'];
	$NSKIN['webhard'] = $G['DINFO']['d_skin_webhard'];
	$NSKIN['mailer'] = 'bootstrap';
	// [2011-12-26] mobile
	$NSKIN['mobile'] = 'bootstrap';

	if (!$NSKIN['main'])
		$NSKIN['main'] = 'bootstrap';
	if (!$NSKIN['webmail'])
		$NSKIN['webmail'] = 'bootstrap';
	if (!$NSKIN['member'])
		$NSKIN['member'] = 'bootstrap';
	if (!$NSKIN['board'])
		$NSKIN['board'] = 'bootstrap';
	if (!$NSKIN['webhard'])
		$NSKIN['webhard'] = 'bootstrap';

	// [2008-10-10] 다중 언어팩
	if ($G_SYS['MULTI_LANG_SUPPORT'] && $G_SYS['nm_lang'] != 'ko')
	{
		$NSKIN['main'] .= '.'.$G_SYS['nm_lang'];
		$NSKIN['webmail'] .= '.'.$G_SYS['nm_lang'];
		$NSKIN['member'] .= '.'.$G_SYS['nm_lang'];
		$NSKIN['board'] .= '.'.$G_SYS['nm_lang'];
		$NSKIN['webhard'] .= '.'.$G_SYS['nm_lang'];

		require_once('include/lang.ko.php');
		require_once('include/lang.'.$G_SYS['nm_lang'].'.php');
	}	// if()

	// [2008-08-19] 스킨버젼 구분
	@include_once(nskindir('main').'/skin_version.php');		// 2.5 이전스킨에는 해당 파일이 없음.
}	// function()

function nskindir($kind, $isroot=false)
{
	global $NSKIN, $G_SYS;

	if (version_compare($NSKIN['ver'], '1.0', 'eq'))
	{
		$path = $kind.'_skin';
		// [2006-06-22] 스킨 1.0을 그대로 유지할때의 조치.
		if ($isroot)
			$path = $G_SYS['URL_ROOT'];
	} else
	{
		if ($kind == 'sadmin' || $kind == 'help')
		{
			$path = $kind;
		} else
		{
			$path = 'skin/'.$kind;
		}	// if()

		if ($NSKIN[$kind] != '')
			$path .= '/'.$NSKIN[$kind];

		if ($isroot)
			$path = $G_SYS['URL_ROOT'].'/'.$path;
	}	// if()

	return $path;
}	// function()

function nskindirroot($kind)
{
	return nskindir($kind, true);
}	// function()

function nskinset($kind, $file, $layout='self', $this_file=NULL)
{
	global $NSKIN, $G_SYS;

	if ($kind == 'sadmin')
	{
		define('NMAIL_SKIN_MAIN_CONTENTS_FILE', 'sadmin/'.$file);		// 컨텐츠
		$NSKIN['view_kind'] = $kind;
	} else if ($kind == 'help')
	{
		define('NMAIL_SKIN_MAIN_CONTENTS_FILE', 'help/'.$file);		// 컨텐츠
		$NSKIN['view_kind'] = $kind;
	} else if ($kind == 'current_dir')
	{
		// [2014-05-19] 현재 폴더아래에 skin 폴더가 오도록 변경.
		$_kind = basename(dirname($this_file));
		define('NMAIL_SKIN_MAIN_CONTENTS_FILE', $_kind.'/skin/'.$file);		// 컨텐츠
		$NSKIN['view_kind'] = '../'.$_kind.'/skin';
	} else
	{
		define('NMAIL_SKIN_MAIN_CONTENTS_FILE', nskindir($kind).'/'.$file);		// 컨텐츠
		$NSKIN['view_kind'] = $kind;
	}	// if()

	// [2011-12-26] mobile
	if ($kind == 'mobile')
	{
		$NSKIN['view_layout'] = nskindir('mobile').'/layout.inc.html';
	} else
	{
		if ($layout == 'self')
		{
			$NSKIN['view_layout'] = nskindir('main').'/layout.inc.html';
		} else if ($layout == 'window_open')
		{
			$NSKIN['view_layout'] = nskindir('main').'/layout_window_open.inc.html';
		} else if ($layout == 'iframe')
		{
			$NSKIN['view_layout'] = nskindir('main').'/layout_iframe.inc.html';
		} else if ($layout == 'modal')
		{
			$NSKIN['view_layout'] = nskindir('main').'/layout_modal.inc.html';
		} else
		{
			error("nskinset() 함수에서 세번째 입력값인 layout 에는 'self, window_open, iframe, modal' 값만 입력할 수 있습니다.   입력된 값 : ".$layout , $code='INPUT');
		}	// if()
	}	// if()
}	// function()

function nskinopendir($kind)
{
	$dirs = array();
	$skindir = 'skin/'.$kind;
	if ($dh = opendir($skindir)) {
		while (($file = readdir($dh)) !== false) {
			if ($file!="." && $file!=".." && is_dir($skindir.'/'.$file))
			{
				$dirs[] = $file;
			}
		}
		closedir($dh);
	}
	sort($dirs);
	return $dirs;
}	// function()

/**
 * 현재 페이지의 CSS class
 *   ex) mail_read.php -> np-mail_read
 *
 * @param string $prefix
 *
 * @return string
 */
function nskin_class_name($prefix = 'np-')
{
	return $prefix . str_replace('.php', '', basename($_SERVER['PHP_SELF']));
}

// [2006-05-29] PHP5에서 한글로 시작되는 파일명에서 발생하는 문제해결.
function basename_fix($filename)
{
	$basename = preg_replace('/^.+[\\\\\\/]/', '', $filename );
	return $basename;
}	// function()


function get_php_ini_path()
{
	ob_start();
	phpinfo(INFO_GENERAL);
	$php_info = ob_get_contents();
	ob_end_clean();

	// [2015-04] PHP5.4.x대응 대체함수 적용
	#foreach (split("\n",$php_info) as $line) {
	foreach (explode("\n",$php_info) as $line) {
		// [2015-04] PHP5.4.x대응 대체함수 적용
		#if (eregi('command',$line)) {
		if (preg_match('/command/i',$line)) {
		  continue;
		}

		if (preg_match('/thread safety.*(enabled|yes)/Ui',$line)) {
		  $thread_safe = true;
		}

		if (preg_match('/debug.*(enabled|yes)/Ui',$line)) {
		  $debug_build = true;
		}

		// [2015-04] PHP5.4.x대응 대체함수 적용
		//if (eregi("configuration file.*(</B></td><TD ALIGN=\"left\">| => |v\">)([^ <]*)(.*</td.*)?",$line,$match)) {
		if (preg_match("/loaded configuration file.*(<\/B><\/td><TD ALIGN=\"left\">| => |v\">)([^ <]*)(.*<\/td.*)?/i",$line,$match)) {
			$php_ini_path = $match[2];
		}
	}

	return $php_ini_path;
}	// function()


function get_mail_dir($m_mail_file_group=NULL, $m_id=NULL, $d_name=NULL)
{
	global $G_SYS, $AUTH;
	if ($m_id === NULL)
	{
		$m_id = $AUTH[auth_m_id];
		$m_mail_file_group = $AUTH[auth_m_mail_file_group];
	}	// if()

	// [2007-05-29]
	if ($d_name === NULL)
		$d_name = $G_SYS[MAIL_DOMAIN];

	if ($m_mail_file_group === '0000' || strlen($m_mail_file_group) != 4)
	{
		$mail_dir = $G_SYS[MAIL_ROOT].'/'.$d_name.'/'.$m_id.'/Maildir';
	} else
	{
		$mail_dir = $G_SYS[MAIL_ROOT].'/'.$d_name.'/'.$m_mail_file_group.'/'.$m_id.'/Maildir';
	}	// if()

	return $mail_dir;
}	// function()


function get_short_date($date)
{
	#if (substr($date, 0, 10) == date('Y-m-d'))		// 동일한 날짜일때만 시간을 표시.
	if (strtotime($date) >= time() - 43200)		// 날짜와 관계없이 12시간 이내는 시간을 표시.
	{
		$return = substr($date, 11, 5);
	} else
	{
		$return = substr($date, 5, 5);
	}	// if()
	return $return;
}	// function()


function get_board_list()
{
	global $db, $TB, $AUTH, $G_SYS, $DEBUG;

	if (!$G_SYS['BOARD_LIST'] && $AUTH['auth_m_no'])
	{
		$q_where = "";
		if ($_GET['bm_no'])
			$q_where = " AND ba.bm_no='".$_GET['bm_no']."' ";


		// [2008-09-12] 회원그룹(단일) -> 회원조직(다중)
			$q = "SELECT mg_no FROM ".$TB['MEMBER_GROUP_REL']." WHERE m_no='".$AUTH['auth_m_no']."'";
			$mgr = db_get_rows($q);
			$mgr_cnt = sizeof($mgr);
			$q_mgr = '';
			if ($mgr_cnt > 0)
			{
				for ($i=0; $i<$mgr_cnt; $i++)
				{
					$q_mgr .= " OR ba.ba_mg_no LIKE '%|".$mgr[$i]['mg_no']."|%' ";
				}	// for()
			}	// if()

		// [2008-03-19] 사용그룹제한 추가
		if ($G_SYS['NM_BOARD_PERM_LIMIT_VIEW'])		// 게시판에 사용권한이 없더라도 목록에서 보여주도록 함.(기본값은 보여주지 않음.)
		{
			$q = "SELECT ba.ba_no, ba.ba_name, ba.bm_no FROM " . $TB['BOARD_ADMIN'] . " ba WHERE ba.d_no='" . $AUTH['auth_d_no'] . "' ".$q_where." ORDER BY ba.ba_sort, ba.ba_name";
			$G_SYS['BOARD_LIST'] = db_get_rows($q);

			$q = "
					SELECT
						ba.ba_no
					FROM
						" . $TB['BOARD_ADMIN'] . " ba
					WHERE
						ba.d_no='" . $AUTH['auth_d_no'] . "'
						AND
							(
								(ba.ba_mg_no IS NULL OR ba.ba_mg_no = '')
								OR ( ba.ba_use_id LIKE '%|".$AUTH['auth_m_id']."|%' ".$q_mgr." )
							)
					";
			$ba_no = db_get_rows($q, true);
			if ($ba_no)
			{
				for ($i=0; $i<sizeof($G_SYS['BOARD_LIST']); $i++)
				{
					if (in_array($G_SYS['BOARD_LIST'][$i]['ba_no'], $ba_no['ba_no']))		// 권한이 있을때
						$G_SYS['BOARD_LIST'][$i]['ba_name'] = '<b>'.$G_SYS['BOARD_LIST'][$i]['ba_name'].'</b>';
				}	// for()
			}	// if()
		} else
		{
			$q = "
					SELECT
						ba.ba_no, ba.ba_name, ba.bm_no
					FROM
						" . $TB['BOARD_ADMIN'] . " ba
					WHERE
						ba.d_no='" . $AUTH['auth_d_no'] . "'
						AND
							(
								(ba.ba_mg_no IS NULL OR ba.ba_mg_no = '')
								OR ( ba.ba_use_id LIKE '%|".$AUTH['auth_m_id']."|%' ".$q_mgr." )
							)
						".$q_where."
					ORDER BY
						ba.ba_sort, ba.ba_name
					";
			$G_SYS['BOARD_LIST'] = db_get_rows($q);
		}	// if()

		// [2013-07-08] 메뉴에 속한 게시판중 권한이 하나도 없을 경우, 메뉴까지 보여주지 않도록 함.   bm_no 가 적용되는 $q_where 는 제외시킴.
		// [2011-01-05] 메뉴관리
		// [2013-08-28] Oracle 예외 처리
		if ($G_SYS[DB] == 'oracle')
		{
			$q = "SELECT bm.bm_no, bm.bm_name, ROW_NUMBER() OVER(PARTITION BY bm.bm_no ORDER BY bm.bm_no) rn FROM ".$TB['BOARD_MENU']." bm, ".$TB['BOARD_ADMIN']." ba 
				WHERE bm.d_no='".$AUTH['auth_d_no']."' AND bm.bm_no=ba.bm_no
							AND
								(
									(ba.ba_mg_no IS NULL OR ba.ba_mg_no = '')
									OR ( ba.ba_use_id LIKE '%|".$AUTH['auth_m_id']."|%' ".$q_mgr." )
								)
				ORDER BY bm.bm_sort, bm.bm_name";
			$q = "SELECT * FROM (".$q.") WHERE rn = 1";
		} else
		{
			$q = "SELECT DISTINCT bm.bm_no, bm.bm_name FROM ".$TB['BOARD_MENU']." bm, ".$TB['BOARD_ADMIN']." ba 
				WHERE bm.d_no='".$AUTH['auth_d_no']."' AND bm.bm_no=ba.bm_no
							AND
								(
									(ba.ba_mg_no IS NULL OR ba.ba_mg_no = '')
									OR ( ba.ba_use_id LIKE '%|".$AUTH['auth_m_id']."|%' ".$q_mgr." )
								)
				ORDER BY bm.bm_sort, bm.bm_name";
		}	// if()


		$G_SYS['BOARD_MENU'] = db_get_rows($q);
	}	// if()

	return $G_SYS['BOARD_LIST'];
}	// function()


function get_bm_name($bm_no)
{
	global $G_SYS, $TB, $AUTH;

	$bm_name = "게시판";
	if ($bm_no)
	{
		if ($G_SYS['BOARD_MENU'])
		{
			for ($i=0; $i<sizeof($G_SYS['BOARD_MENU']); $i++)
			{
				if ($G_SYS['BOARD_MENU'][$i]['bm_no'] == $bm_no)
				{
					$bm_name = $G_SYS['BOARD_MENU'][$i]['bm_name'];
				}	// if()
			}	// for()
		} else
		{
			$q = "SELECT bm_name FROM ".$TB['BOARD_MENU']." WHERE bm_no='".$bm_no."' AND d_no='".$AUTH['auth_d_no']."'";
			$bm_name = db_get_one($q);
		}	// if()
	}	// if()

	return $bm_name;
}	// function()

/**
 * 비밀번호 암호화
 *
 * @param string $pwd
 * @param string $id
 * @param string $pwd_enc
 *
 * @return string|null
 */
function pwd_enc($pwd, $id='', $pwd_enc='')
{
	global $db, $G_SYS;
	if ($G_SYS[PWD_ENC] == 'BCRYPT')
	{
		$pwd = password_hash($pwd, PASSWORD_BCRYPT, array('cost' => 10));
	} else if ($G_SYS[PWD_ENC] == 'MYSQL')
	{
		$q = "SELECT PASSWORD(?)";
		$pwd = db_get_one($q, array(addslashes($pwd)));
	} else if ($G_SYS[PWD_ENC] == 'MYSQL_OLD')
	{
		$q = "SELECT OLD_PASSWORD(?)";
		$pwd = db_get_one($q, array(addslashes($pwd)));
	} else if ($G_SYS[PWD_ENC] == 'ORACLE_FUNC_ENCRYPT')
	{
		if (!$id)
			$id = 'NULL';
		$q = "SELECT ENCRYPT(?, ?) FROM DUAL";
		$pwd = db_get_one($q, array(addslashes($id), addslashes($pwd)));
	} else if ($G_SYS[PWD_ENC] == 'CRYPT')
	{
		if ($pwd_enc)
		{
			$pwd = crypt($pwd, $pwd_enc);
		} else
		{
			$pwd = crypt($pwd);
		}	// if()
	} else if ($G_SYS[PWD_ENC] == 'CRYPT2')
	{
		if (strlen($pwd) <= 8)
		{
			$pwd = crypt($pwd, $id);
		} else if (strlen($pwd) <= 16)
		{
			$pwd = crypt(substr($pwd, 0, 8), $id).crypt(substr($pwd, 8), $id);
		} else
		{
			$pwd = crypt(substr($pwd, 0, 8), $id).crypt(substr($pwd, 8, 8), $id).crypt(substr($pwd, 16), $id);
		}	// if()
	} else
	{
		$pwd = md5($pwd);
	}	// if()
	return $pwd;
}	// function()


/**
	페이지 로딩 화면 출력하기
		: 클라이언트에서 서버로 데이타 전송이 끝난뒤 실제 프로그램을 처리하는 시점부터 표시됨.

	ex)
		page_loading_view("메일을 발송하고 있습니다.");
*/
function page_loading_view($msg)
{
	global $G_SYS, $NSKIN, $G, $nmpath;

	// [2012-04-30] 모바일웹
	if (!$_COOKIE['nm_mobile'])
	{
		include(nskindir('webmail').'/page_loading_view.inc.html');

		// [2013-10-10] output_buffering
		$output_buffering = ini_get('output_buffering');
		if ($output_buffering > 0 && is_numeric($output_buffering))
			echo str_repeat(" ", $output_buffering);

		flush();
		$G_SYS['PAGE_LOADING_VIEW_EXEC'] = true;
	}	// if()
}	// function()


/**
	페이지 로딩 화면 숨기기

	ex)
		page_loading_view_hidden();
*/
function page_loading_view_hidden()
{
	global $G_SYS;
	if ($G_SYS['PAGE_LOADING_VIEW_EXEC'])
	{
		?>
			<script language="JavaScript">
			<!--
				nm_page_loading_view_isoff = 1;
				document.all.nm_page_loading_view.style.display = 'none';
			//-->
			</script>
		<?
	}	// if()
}	// function()


/**
	파일크기를 읽기쉽게 변환하기

	[2005-04-28]
*/
function filesize_human($byte, $sosu=2, $zero_string=NULL)
{
	/*
	if ($byte  < 1024)
	{
		$h1 = $byte;
		$h2 = "";
	} else
	*/
	if ($byte == 0 && $zero_string != NULL)
	{
		return $zero_string;
	} else if ($byte < 1048576)
	{
		$h1 = $byte / 1024;
		$h2 = "K";
	} else if ($byte < 1073741824)
	{
		$h1 = $byte / 1048576;
		$h2 = "M";
	} else if ($byte < 1099511627776) {
		$h1 = $byte / 1073741824;
		$h2 = "G";
	} else
	{
        $h1 = $byte / 1099511627776;
        $h2 = "T";
	}	// if()

	return number_format(ceil($h1*100)/100, $sosu).$h2;
}	// function()





###############################################################################
# Pear - DataBase
###############################################################################


function set_db()
{
	global $db, $G_SYS, $DEBUG;

	if ($db != NULL) return;

	require_once("DB.php");

	$DEBUG['db_connetion']['start'] = getmicrotime();

	$db = DB::connect($G_SYS[DB_DSN]);

	if (DB::isError($db)) {
		// [2012-05-24] 디비 연결 오류도 로그에 남김
		error("디비에 연결할 수 없습니다.<br><br>디비 계정정보를 확인해보시기 바랍니다.<br><br>" . $db->getMessage() . $db->backtrace[0]['args'][4], 'DB');
		//die ("디비에 연결할 수 없습니다.<br><br>디비 계정정보를 확인해보시기 바랍니다.<br><br>" . $db->getMessage() . $db->backtrace[0]['args'][4]);
	}


	// fetch 후 결과값을 $row['id'] 형식으로 필드이름을 키값으로 가지게 함.
	$db->setFetchMode(DB_FETCHMODE_ASSOC);

	// 테이블, 필드명을 소문자로 반환함.
	$db->setOption('portability', DB_PORTABILITY_LOWERCASE);

	// [2006-08-18] 디비 언어셋이 지정되었을때는 접속시마다 선언함.
	if ($G_SYS[DB_CHARSET] != '')
		db_query("SET NAMES ".$G_SYS[DB_CHARSET]);

	// [2024-04-17] MySQL sql_mode
	if ($G_SYS[DB_SQL_MODE] != '') {
		db_query("SET sql_mode = '" . $G_SYS[DB_SQL_MODE] . "'");
	}
}	// function()


function unset_db()
{
	global $db, $DEBUG;

	if ($db == NULL) return;

	$db->disconnect();
	unset($db);

	$DEBUG['db_connetion']['exec'] = getmicrotime() - $DEBUG['db_connetion']['start'];

}	// function()


/**
 * 테이블 자동 증가값
 *  *
 * @param string $table
 * @param bool $is_binding   db_create() 등에서 바인딩할 경우 true, NULL 문자열이 아닌 null 응답
 *
 * @return string
 */
function db_sqc_next($table, $is_binding=false)
{
	global $G_SYS;
	if ($G_SYS[DB] == 'oracle')
	{
		$return = 'sqc_'.$table.'.NEXTVAL';
	} else
	{
		if ($is_binding) {
			$return = null;
		} else {
			// [2007-08-23] MySQL 5.X 에서 경고 예방.
			//$return = "''";
			$return = "NULL";
		}
	}	// if()
	return $return;
}	// function()


/**
 * 테이블 자동 증가하여 저장된 값
 *
 * @param string $table
 *
 * @return string
 */
function db_sqc_curr($table)
{
	global $G_SYS;
	if ($G_SYS[DB] == 'oracle')
	{
		$return = db_get_one("SELECT sqc_".$table.".CURRVAL FROM dual");		// 삽입된 데이타 키
	} else
	{
		$return = db_get_one("SELECT LAST_INSERT_ID()");		// 삽입된 데이타 키
	}	// if()
	return $return;
}	// function()


/**
	[DB] addslashes() 된 상태의 쿼리문을 Oracle 용에 맞게 변환.
		php.ini) magic_quotes_gpc = On
*/
function db_query_quote($query, $is_prepare=false)
{
	global $G_SYS;
	if ($G_SYS[DB] == 'oracle')
	{
		// [2006-03-10] oracle 에서 메일 데이타 저장시에만 쿼터가 붙던 오류 해결.
		#$query = stripslashes(str_replace("\\'", "''", $query));
		if ($is_prepare)
		{
			$query = stripslashes($query);
		} else
		{
			// [2010-03-09] TIBERO
			if ($G_SYS[DB_ODBC] == 'tibero')
			{
				$query = stripslashes(str_replace("\\'", "''", $query));
			} else
			{
				// [2009-10-30] \ 가 포함된 경우 예외처리 추가
				//$query = stripslashes(str_replace("\\'", "''", $query));
				//$query = stripslashes(str_replace(array("\\\\", "\\'"), array("''", "\\"), $query));
				// [2011-11-09] Oracle 버젼에서 문제 발생하여 원상복구.
				$query = stripslashes(str_replace("\\'", "''", $query));
			}	// if()
		}	// if()
	}	// if()
	return $query;
}	// function()


// [2005-06-28] 디비 쿼리후 결과값에서 에러발생시 실행중단.
function db_result_check($res, $is_error_exit=true)
{
	$bt = $res->backtrace[sizeof($res->backtrace)-1];
	// [2007-08-21] 디비쿼리시 에러로그를 남길때 쿼리문이 너무 길 경우 잘라서 남기도록 함.
	if (DB::isError($res))
	{
		if (strlen($res->userinfo) > 2048)
			$res->userinfo = substr($res->userinfo, 0, 1024).'...'.substr($res->userinfo, (strlen($res->userinfo)-1024));

		// [2014-05-28] 에러 발생시 중단하지 않고, 에러 메세지를 리턴하도록 함수 인자값 추가.  리턴값이 true 가 아닐 경우, 에러임.
		if ($is_error_exit)
		{
			error("Message : ".$res->getMessage().", Source : ".$bt['file']." ( ".number_format($bt['line'])." line ), Return messages : ".$res->userinfo, 'ERROR');
		} else
		{
			return $res->userinfo;
		}
	} else{
		return true;
	}	// if()
}	// function()

/**
 * DB SELECT - 첫번째 컬럼 조회
 *
 * @param string $sql
 * @param array  $bindings
 *
 * @return string
 */
function db_get_one($sql, $bindings=array())
{
	global $db, $G_SYS;
	$_start = getmicrotime();

	$one = $db->getOne(db_query_quote($sql), db_binding_prefilter($bindings));

	db_result_check($one);

	$exec_time = getmicrotime() - $_start;
	db_debug(__FUNCTION__, $exec_time, $sql, $bindings);
	if ($G_SYS['DEBUG_QUERY'] == 'VIEW')
		echo '<xmp>db_get_one(' . round($exec_time, 4) . 'sec) : ' . $sql . '</xmp>';
	if ($exec_time > $G_SYS['DEBUG_QUERY_OVER_TIME'])
		errorlog("[db_get_one]Time : " . round($exec_time, 4) . "sec, Query : ".$sql, 'DEBUG');
	return $one;
}	// function()

/**
 * DB SELECT - 첫번째 행 조회
 *
 * @param string $sql
 * @param array  $bindings
 *
 * @return array|false
 */
function db_get_row($sql, $bindings=array())
{
	global $db, $G_SYS;
	$_start = getmicrotime();

	$res = $db->Query(db_query_quote($sql), db_binding_prefilter($bindings));
	db_result_check($res);
	$row = array();
	$res->fetchInto($row);
	$res->free();

	// [2009-05-13] Oracle 에서 값이 없을때 반환값을 NULL 대신 false 로 처리
	if ($row === NULL || (is_array($row) && sizeof($row) == 0))
		$row = false;

	$exec_time = getmicrotime() - $_start;
	db_debug(__FUNCTION__, $exec_time, $sql, $bindings);
	if ($G_SYS['DEBUG_QUERY'] == 'VIEW')
		echo '<xmp>db_get_row(' . round($exec_time, 4) . 'sec) : ' . $sql . '</xmp>';
	if ($exec_time > $G_SYS['DEBUG_QUERY_OVER_TIME'])
		errorlog("[db_get_row]Time : " . round($exec_time, 4) . "sec, Query : ".$sql, 'DEBUG');
	return $row;
}	// function()

/**
 * DB SELECT - 모든 행 조회
 *
 * @param string $sql
 * @param int    $from
 * @param int    $count
 * @param bool   $is_fieldtokey
 * @param array  $bindings
 *
 * @return array
 */
function db_get_rows($sql, $from=0, $count=0, $is_fieldtokey=false, $bindings=array())
{
	global $db, $G_SYS;
	$_start = getmicrotime();

	if ($count > 0)
	{
		$res = $db->limitQuery(db_query_quote($sql), $from, $count, db_binding_prefilter($bindings));
		$sql = $db->last_query;
	} else
	{
		$res = $db->Query(db_query_quote($sql), db_binding_prefilter($bindings));
		// 함수 입력값이 바뀌는 과정에서 db_get_rows($q, true) 와의 호환을 위함.
		if ($from === true)
		{
			$is_fieldtokey = true;
		}	// if()
	}	// if()
	db_result_check($res);
	$rows = array();
	$row = array();
	while ($res->fetchInto($row))
	{
		$rows[] = $row;
	}	// while()
	$res->free();

	if ($is_fieldtokey)
	{
		$rows = db_rows_fieldtokey($rows);
	}	// if()

	$exec_time = getmicrotime() - $_start;
	db_debug(__FUNCTION__, $exec_time, $sql, $bindings);
	if ($G_SYS['DEBUG_QUERY'] == 'VIEW')
		echo '<xmp>db_get_rows(' . round($exec_time, 4) . 'sec) : ' . $sql . '</xmp>';
	if ($exec_time > $G_SYS['DEBUG_QUERY_OVER_TIME'])
		errorlog("[db_get_rows]Time : " . round($exec_time, 4) . "sec, Query : ".$sql, 'DEBUG');
	return $rows;
}	// function()

/**
 * DB QUERY - 제한된 수만큼 적용되는 쿼리
 *   - ex) 로그 분석시 같은 s_msgid 내에서는 1건만 업데이트 적용
 *
 * @param string $sql
 * @param int    $limit
 * @param array  $bindings
 *
 * @return string|true|null
 */
function db_query_limit($sql, $limit, $bindings=array())
{
	if (!is_numeric($limit))
		errorlog("[db_query_limit]limit is null, Query : ".$sql, 'INPUT');

	return db_query($sql, $bindings, $limit);
}	// function()

/**
 * DB QUERY - 실행 (INSERT, UPDATE, DELETE, DDL)
 *
 * @param string $sql
 * @param array  $bindings
 * @param int    $limit
 * @param bool   $is_error_exit
 *
 * @return string|true|null
 */
function db_query($sql, $bindings=array(), $limit=NULL, $is_error_exit=true)
{
	global $db, $G_SYS;
	$_start = getmicrotime();

	// [2013-10-29] DELETE/UPDATE 시 MySQL 에서만 LIMIT 1 사용 가능하도록 함.
	if ($limit != NULL && is_numeric($limit) && $G_SYS[DB] != 'oracle')
		$sql .= " LIMIT ".$limit;

	if (sizeof($bindings) > 0)
	{
		$sth = $db->prepare($sql);

		$bindings = db_binding_prefilter($bindings);

		// [2006-09-29] TIBERO
		if ($G_SYS[DB_ODBC] == 'tibero')
		{
		   $db->stmt = odbc_prepare($db->connection, $sql);
		   if (is_array($bindings))
		   {
				foreach ($bindings as $key=>$val)
				{
					// [2024-08-05] Tibero 저장시 지원되지 않는 문자열 치환
					if ($val != strip_tags($val)) {
						// U+00A0 NO-BREAK SPACE -> &nbsp; (HTML entity)
						$bindings[$key] = str_replace(' ', '&nbsp;', $val);
					} else {
						// U+00A0 NO-BREAK SPACE -> U+0020 SPACE
						$bindings[$key] = str_replace(' ', ' ', $val);
					}

					// [2007-03-22] 시작과 끝이 작은 따옴표(')로 둘러쌓인 경우 "odbc_execute(): Can't open file" 에러가 발생하므로 마지막라인에 빈 공백을 하나 추가해주어야 함.
					// [2025-11-18] 지원하지 않는 문자열 치환후, 공백 추가하도록 개선
					if (strlen($val) > 1 and $val{0} == "'" and $val{strlen($val)-1} == "'")
						$bindings[$key] .= ' ';
				}	// foreach()
		   }	// if()
		   $result = odbc_execute($db->stmt,$bindings);

			if (!$result) {
				$result = $db->odbcRaiseError(); // XXX ERRORMSG
			}
			// Determine which queries that should return data, and which
			// should return an error code only.
			if (DB::isManip($sql)) {
				$db->affected = $result; // For affectedRows()
				$result = DB_OK;
			}
			$db->affected = 0;
			//$result = $result;

			if ($result === DB_OK || DB::isError($result)) {
				$res = $result;
			} else {
				// [2016-01-11] PHP 5버전대에서 Ampersand가 DEPRECATED처리되어 &삭제
				#$tmp =& new DB_result($this, $result);
				$tmp = new DB_result($this, $result);
				$res = $tmp;
			}
		} else
		{
			$res = $db->execute($sth, $bindings);
		}	// if()
	} else
	{
		$res = $db->Query(db_query_quote($sql));
	}	// if()

	// [2014-05-28] 에러 발생시 중단하지 않고, 에러 메세지를 리턴하도록 함수 인자값 추가.  리턴값이 true 가 아닐 경우, 에러임.
	$result_check = db_result_check($res, $is_error_exit);

	// [2006-09-15] TIBERO
	if ($G_SYS[DB_ODBC] == 'tibero')
		$db->commit();

	$exec_time = getmicrotime() - $_start;
	db_debug(__FUNCTION__, $exec_time, $sql, $bindings);
	if ($G_SYS['DEBUG_QUERY'] == 'VIEW')
		echo '<xmp>db_query(' . round($exec_time, 4) . 'sec) : ' . $sql . '</xmp>';
	if ($exec_time > $G_SYS['DEBUG_QUERY_OVER_TIME'])
		errorlog("[db_query]Time : " . round($exec_time, 4) . "sec, Query : ".$sql, 'DEBUG');

	return $result_check;
}	// function()

// [2013-10-30] 마지막으로 실행된 쿼리문에서 INSERT/UPDATE/DELTE 가 적용된 데이타 수를 반환.  없을 땐 0
function db_query_row_count()
{
	global $db;
	return $db->affectedRows();
}	// function()


###############################################################################



	// [2005-07-16] 널,빈문자열,빈배열일때 true 값 반환.  ["0", 0 은 false]
		/*
			ex)
				NULL => bool(true)
				string(0) "" => bool(true)
				string(1) " " => bool(false)
				string(1) "0" => bool(false)
				int(0) => bool(false)
				int(1) => bool(false)
				bool(true) => bool(false)
				bool(false) => bool(true)
				string(1) "" => bool(false)
				array(0) { } => bool(true)
				array(1) { [0]=> string(1) "a" } => bool(false)
		*/
		function isblank($str)
		{
			if (is_array($str))
			{
				if (sizeof($str) > 0)
				{
					return false;
				} else
				{
					return true;
				}	// if()
			} else
			{
				if ($str == '' && !is_numeric($str))
				{
					return true;
				} else
				{
					return false;
				}	// if()
			}	// if()
		}	// function()


	// [2005-07-13] 디비질의후 결과데이터의 배열을 필드명을 키값으로 지정함.
		function db_rows_fieldtokey($rows)
		{
			$return = array();
			for ($i=0; $i<sizeof($rows); $i++)
			{
				foreach ($rows[$i] as $key=>$val)
				{
					$return[$key][$i] = $val;
				}	// foreach()
			}	// for()

			return $return;
		}	// function()

		function db_rows_keytofield($rows)
		{
			$return = array();
			foreach ($rows as $key1=>$val1)
			{
				foreach ($rows[$key1] as $key=>$val)
				{
					$return[$key][$key1] = $val;
				}	// foreach()
			}	// foreach()

			return $return;
		}	// function()

	// [2005-07-13] 쿼리스트링에서 특정 값 제거후 반환.(문자열 형태)
		// 'chk_mf_no[] => chk_mf_no%5B%5D'형태의 배열때문에 키값 비교시 urldecode()처리 필요함.
		function get_qs()
		{
			$args= func_get_args();

			$qs = array();
			$qs_ex = array();

			if (!empty($_SERVER['QUERY_STRING']))
			{
				$qs_ex = explode('&', $_SERVER['QUERY_STRING']);
				for ($i=0; $i<sizeof($qs_ex); $i++)
				{
					list($key, $val) = explode('=', $qs_ex[$i]);
					$_as = array_search(urldecode($key), $args);
					if (($_as === false || $_as === NULL) && $val != '')
					{
						$qs[] = $key.'='.rawurlencode(urldecode($val));
					}	// if()
				}
			}	// if()

			$result = implode('&', $qs);

			if (empty($result))
				$result = 'tmp=tmp';

			return $result;
		}	// function()


	// [2005-07-13] 쿼리스트링에서 특정 값 제거후 반환.(hidden input 폼태그 형태)
		function get_qs_input_hidden()
		{
			$args= func_get_args();

			$qs = '';
			$qs_ex = array();

			if (!empty($_SERVER['QUERY_STRING']))
			{
				$qs_ex = explode('&', $_SERVER['QUERY_STRING']);
				for ($i=0; $i<sizeof($qs_ex); $i++)
				{
					list($key, $val) = explode('=', $qs_ex[$i]);
					$_as = array_search(urldecode($key), $args);
					if (($_as === false || $_as === NULL) && $val != '')
					{
						// [2005-08-04] get방식의 폼에서 두번째 검색시부터 데이타가 중복 urlencode()되는 문제해결.
						#$qs .= '<input type=hidden name='.$key.' value="'.htmlspecialchars_fix($val).'">'."\n";
						// [2008-11-20] PHP 5.2.6AnNyung-51928 에서 urldecode 사용시 mb_id=1 을 mb_id=11 로 해석하는 문제가 발생하는 것 보완
						//$qs .= '<input type=hidden name='.$key.' value="'.htmlspecialchars_fix(urldecode($val)).'">'."\n";
						if (strpos($val, '%') !== false)
							$val = urldecode($val);
						// [2010-10-04] 대량메일발송시 name 값이 배열인 경우 처리
						if (strpos($key, '%') !== false)
							$key = urldecode($key);
						$qs .= '<input type=hidden name='.$key.' value="'.htmlspecialchars_fix($val).'">'."\n";
					}	// if()
				}
			}	// if()

			return $qs;
		}	// function()


	// [2006-01-04] 쿼리스트링에서 특정 값을 영구히 제거함.
		function cut_qs()
		{
			$args= func_get_args();
			if (!empty($_SERVER['QUERY_STRING']))
			{
				$cut = '('.implode('|', $args).')';
				$_SERVER['QUERY_STRING'] = preg_replace("/(^".$cut."\=|&".$cut."\=)+([^&]+)/i", "", $_SERVER['QUERY_STRING']);
				if (strpos($_SERVER['QUERY_STRING'], '&') === 0)
					$_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'], 1, strlen($_SERVER['QUERY_STRING'])-1);
			}	// if()
		}	// function()


	// [2005-07-13] 에러처리
		/*
			-. code
				INPUT
				DB
				FILE
				MAILPARSE
				MAILSERVER

			-. 시스템설정에 따라 사용자에게 보여주는 부분은 생략할 수 있도록 함.
		*/
		function error($msg, $code='INPUT', $isexit=true, $isback=false, $isview=true)
		{
			global $G_SYS, $AUTH;

			// file func only
			$msg = str_replace("\\n", "\n", $msg);

			// [2025-09-30] 로그 디버깅
			if (is_local()) {
				$message = 'error (' . $code . ') : ' . $msg;

				$log_level = 'error';
				if (in_array($code, array('DEBUG', 'WEBMAILSEND'))) {
					$log_level = 'warn';
				}

				$GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'] .= '<script>console.' . $log_level . '('
						. json_encode(
								$message,
								version_compare(PHP_VERSION, '5.4.0', '>=')
										? JSON_UNESCAPED_UNICODE
										: null
						)
						. ');</script>' . PHP_EOL;
			}

			if ($isview)
			{
				// 웹상일때와 쉘상일때 에러 구분 표시
				if ($_SERVER['DOCUMENT_ROOT'] || $_SERVER['SERVER_SOFTWARE'])
				{
					page_loading_view_hidden();

					// [2024-09-04] DB 에러는 로그로만 남기고, 보안 강화를 위해 운영시 화면 출력 생략
					if (strpos($msg, 'DB Error') !== false && ! is_local()) {
						echo "<font color=red>[<b>Nmail DB Error - " . $code . "</b>]<br>"
							. "DB 에러가 발생했습니다.<br>"
							. "서버에서 weblogs" . DIRECTORY_SEPARATOR . "error-" . date('Ymd')
							. ".txt 로그 파일을 확인해주세요.</font><br>";
					} else {
						echo("<br><font color=red>[<b>Nmail Error - " . $code . "</b>]<br>" . nl2br($msg)
							. "</font><br>");
					}
				} else
				{
					echo ("\n[Nmail Error - " . $code . "]\n" . $msg . "\n");
				}	// if()
			}	// if()

			// [2008-02-26] 로그에 로그인한 아이디 추가.
			//$message = '[' . date('Y-m-d H:i:s') . '] "' . $_SERVER['REMOTE_ADDR'] . '" "' . $_SERVER['REQUEST_URI'] . '" [' . $code . '] "' . str_replace("\n", '  ', str_replace("\r", '', $msg)) . "\"\n";
			$message = '[' . date('Y-m-d H:i:s') . '] "' . $_SERVER['REMOTE_ADDR'] . '" "' . $_SERVER['REQUEST_URI'] . '" [' . $code . '] "' . str_replace("\n", '  ', str_replace("\r", '', $msg)) . '" "' . $G_SYS['MAIL_DOMAIN'] . '" "' . $AUTH['auth_m_id'] . "\"\n";
			if ($code == 'FILTER_SPAM')
			{
				$logfile_pre = 'spam';
			} else if ($code == 'SPAM_NO')
			{
				$logfile_pre = 'spam_no';
			} else if ($code == 'SPAM_REPORT')
			{
				$logfile_pre = 'spam_report';
			} else if ($code == 'FILTER_VIRUS')
			{
				$logfile_pre = 'virus';
			} else if ($code == 'DEBUG')
			{
				$logfile_pre = 'debug';
			} else if ($code == 'TEST')
			{
				$logfile_pre = 'test';
			} else if ($code == 'AUTO_FILTER')
			{
				$logfile_pre = 'autosort';
			} else if ($code == 'AUTO_REPLY')
			{
				$logfile_pre = 'autoreply';
			} else if ($code == 'AUTO_DEL')
			{
				$logfile_pre = 'autodel';
			} else if ($code == 'MEMBER')
			{
				$logfile_pre = 'member';
			} else if ($code == 'MAILDEL')
			{
				$logfile_pre = 'maildel';
			} else if ($code == 'WEBMAILSEND')
			{
				$logfile_pre = 'webmailsend';
			} else if ($code == 'FILEDEL')
			{
				$logfile_pre = 'filedel';
			} else if ($code == 'SECURITY')
			{
				$logfile_pre = 'security';
			} else
			{
				$logfile_pre = 'error';
			}	// if()
			#$destination = $G_SYS[PAGE_ROOT] . '/data/logs/'.$logfile_pre.'-' . date('Ymd') . '.txt';
			$destination = $G_SYS['WEBLOGS_DIR'] . '/'.$logfile_pre.'-' . date('Ymd') . '.txt';
			$fe = file_exists($destination);
			error_log($message, 3, $destination);
			// 로그파일이 처음 생성될때 퍼미션 변경.
			if (!$fe && !$_SERVER['DOCUMENT_ROOT'] && !$_SERVER['SERVER_SOFTWARE'])
			{
				chmod($destination, 0666);
			}	// if()
			if ($isback == true)
			{
				echo "<script language=\"JavaScript\"> <!-- history.back(); //--> </script>";
			}	// if()
			if ($isexit == true)
			{
				// [2025-09-30] 로그 디버깅
				if (isset($GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'])) {
					echo $GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'];
				}

				exit;
			}	// if()
		}	// function()

		function errorlog($msg, $code)
		{
			return error($msg, $code, false, false, false);
		}	// function()


	// [2005-05-27] 변수 출력용
		function vd($var, $title = ''){
			if ($title)
			{
				echo '<b><font color=blue>[' . htmlspecialchars_fix($title) . ']</font></b>';
			}	// if()

			if (extension_loaded('xdebug')) {
				ini_set("xdebug.var_display_max_children", '-1');
				ini_set("xdebug.var_display_max_data", '-1');
				ini_set("xdebug.var_display_max_depth", '-1');
				var_dump($var);
			} else {
				echo "<xmp>";
				var_dump($var);
				echo "</xmp>";
			}
		}


	// [2004-02-19] htmlspecialchars_fix() 사용시 "햏", "쀍"등 일부 한글이 깨지는 현상방지.
		function htmlspecialchars_fix($str)
		{
			#$trans = array("&" => "&amp;", "\"" => "&quot;", "＇" => "&#039;", "<"=>"&lt;", ">"=>"&gt;");
			// [2005-08-05] 작은따옴표 수정
			#$trans = array("\"" => "&quot;", "＇" => "&#039;", "<"=>"&lt;", ">"=>"&gt;");
			// [2005-08-19] textarea 내에 값을 넣을 경우를 위해 '&'추가.

			// [2005-10-06] &#48577; 형태에서 "&"는 변환하지 않음.  (08-19일자 수정전으로 되돌림)
			#$trans = array("&" => "&amp;", "\"" => "&quot;", "'" => "&#039;", "<"=>"&lt;", ">"=>"&gt;");
			$trans = array("\"" => "&quot;", "＇" => "&#039;", "<"=>"&lt;", ">"=>"&gt;");
			$str = strtr($str, $trans);
			return $str;
		}	// function()


	// [2003-10-09] 배열갯수가져오기 - 배열이 아닌 일반 변수일때 '1'이 반환되는 점은 방지하기 위함.
		function getarraycount($array){
			return is_array($array) ? sizeof($array) : 0;
		}



	// Debugging
		// [2003-05-30]
		function getmicrotime(){
			list($usec, $sec) = explode(" ",microtime());
			return ((float)$usec + (float)$sec);
		}



	// [2005-07-18] 정렬용 데이타 가져오기
		/*
			ex)
				$order_set = get_order($_GET['s_key'], $_GET['s_key2']);
				$order_img = get_order_img(array_keys($CFG['MAIL_LIST_FIELD']), $_GET['s_key'], $_GET['s_key2'], false, 'common_skin');		// array
		*/
		function get_order($s_key_list, $s_key_default, $s_key, $s_key2)
		{
			global $G_SYS;

			if ($s_key)
			{
				$_as = array_search($s_key, $s_key_list);
				if ($_as !== false && $_as !== NULL)
				{
					$s_key_set = $s_key;
				} else
				{
					error("다음은 정렬할 수 없는 필드명입니다. => [" . $s_key . "]", "INPUT");
				}	// if()
			} else
			{
				$s_key_set = $s_key_default;
				// [2005-10-13] 기본값일때도 정렬 아이콘 표시.
				$G_SYS['__GET_ORDER_S_KEY_DEFAULT'] =$s_key_default;
				$s_key2 = ($s_key2) ? $s_key2 : 'DESC';
			}	// if()
			$q_order = $s_key_set;

			if($s_key2)
			{
                // [2020-02] KVE SQL Injection 개선
                #$q_order .= ' ' . $s_key2;
                if($s_key2 == 'ASC'){
                    $q_order .= ' ASC';
                }else{
                    $q_order .= ' DESC';
                }
				if($s_key2 == 'ASC')
				{
					$s_key2_set = 'DESC';
				}else
				{
					$s_key2_set = 'ASC';
				}
			}else
			{
				$q_order .=  ' DESC';
				$s_key2_set = 'ASC';
			}
			return array('query' => $q_order, 'key' => $s_key_set, 'set' => $s_key2_set);
		}	// function()


		/**
			정렬이미지 가져오기

			ex)
					$order_field = array(
							"sc2.sc2_no",
							"sc2.sc2_name",
							"sc2.sc2_regdate"
							);
					$order_img = get_order_img($order_field, $s_key, $s_key2);		// array
		*/
		function get_order_img($field, $s_key, $s_key2, $idx_is_no=false, $img_dir='img')
		{
			global $G_SYS;

			// [2006-03-22] 이미지 디렉토리 통합
			if ($img_dir == 'common_skin')
				$img_dir = 'img';

			// [2005-10-13] 기본값일때도 정렬 아이콘 표시.
			if (!$s_key)
			{
				$s_key = $G_SYS['__GET_ORDER_S_KEY_DEFAULT'];
				$s_key2 = 'DESC';
			}	// if()

			for($i=0; $i<sizeof($field); $i++)
			{
				$idx = ($idx_is_no) ? $i : $field[$i];

				if($s_key == $field[$i])
				{
					if($s_key2 == "DESC")
					{
						$return[$idx] = ' <img src='.nskindirroot('webmail').'/' . $img_dir . '/order_desc.gif align=absmiddle border=0 alt="오름차순으로 정렬하기">';
					}else
					{
						$return[$idx] = ' <img src='.nskindirroot('webmail').'/' . $img_dir . '/order_asc.gif align=absmiddle border=0 alt="내림차순으로 정렬하기">';
					}
				}else
				{
					$return[$idx] = NULL;
				}
				//echo "<HR>" . $idx . " - " . $s_key . " - " . $s_key2 . " - " . $return[$idx];
			}

			return $return;

		} // end func


		/**
		 * 배열값들을 콤마로 구분하여 나열하기
		 *   ex) $q_where = set_array2comma(" WHERE ba_no in (", ") ", array_keys($prefix));
		 *
		 * @param string $prefix
		 * @param string $suffix
		 * @param array $array_key
		 * @param string $fix
		 * @param string $comma
		 * @param bool $data_only_key  키 값만 허용 (기본)
		 *
		 * @return string
		 */
		function set_array2comma($prefix, $suffix, $array_key, $fix="'", $comma=",", $data_only_key = true)
		{
			$return = $prefix;
			for($i=0; $i<sizeof($array_key); $i++)
			{
				if (
					$data_only_key
					&& $array_key[$i] !== null
					&& $array_key[$i] !== ''
					&& ! preg_match('/^[a-zA-Z0-9\-_.]+$/', $array_key[$i])
				) {
					security_log('비정상 접근이 차단되었습니다.', 'set_array2comma()', $array_key[$i]);
				}
				$return .= $fix . $array_key[$i] . $fix;
				if(($i+1) != sizeof($array_key)) $return .= $comma;
			}
			$return .= $suffix;

			return $return;
		}


		function set_text_fetch($text)
		{
			global $_SERVER, $AUTH, $G, $G_SYS, $G_CM, $G_MB, $G_BOARD, $CFG;

			$text = text_fetch("_SERVER", $_SERVER, $text);
			$text = text_fetch("AUTH", $AUTH, $text);
			$text = text_fetch("G", $G, $text);
			$text = text_fetch("G_SYS", $G_SYS, $text);
			$text = text_fetch("G_CM", $G_CM, $text);
			$text = text_fetch("G_MB", $G_MB, $text);
			$text = text_fetch("G_BOARD", $G_BOARD, $text);
			$text = text_fetch("CFG", $CFG, $text);

			return $text;
		}	// function()


		/**
			string text_fetch (string var_name, array var, string text )

			[2002-05-24]
			ex) $body = text_fetch("rm", $rm, $body);
		*/
		function text_fetch($var_name, $var, $text)
		{
			if (!is_array($var)||(sizeof($var) < 1)) return $text;

			$_var_source = array();
			$_var_dest = array();
			$_var_cnt = 0;
			while( list($_key_, $_val_) = each($var) )
			{
				// [2025-12-22] 개발 환경에서 $G_SYS 등에 익명함수가 있을 경우, 비밀번호 재발급 메일 에러 방지
				if ( ! is_object($_val_))
				{
					$_var_source[$_var_cnt] = preg_quote("/<?= \$" . $var_name . "[" . $_key_ . "] ?>/");        // <?
					$_var_dest[$_var_cnt]   = $_val_;
					//echo "<hr><xmp>[$_var_cnt] " . $_var_source[$_var_cnt] . "</xmp>";
					//echo "<hr><xmp>[$_var_cnt] " . $_var_dest[$_var_cnt] . "</xmp>";
					$_var_cnt++;
				}
			}	// while()

			$text = preg_replace($_var_source, $_var_dest, $text);
			//echo "<hr><xmp>" . $text . "</xmp>";
			return $text;
		} // end func


		function system_msg($msg)
		{
			global $_SERVER;

			echo "<HR><H4>[System error] <a href='" . $_SERVER["REQUEST_URI"] . "' target='_blank'>" . $_SERVER["REQUEST_URI"] . "</a></H4>";
			echo "<font color=red>" . $msg . "</font><br><HR>";
			unset_db();
			exit;
		}	// function();


		function set_q_search(&$word)
		{
			$word = str_replace("%", "", trim($word));		// % 기호 삭제
			// [2005-07-18] magic_quotes_runtime 을 켜두므로 괜찮음.
			//$word = str_replace("'", "", $word);		// ' 기호 삭제
			return $word;
		}	// function()


		/** mailto url
			- 이메일이 없을 경우 링크를 걸지 않음.
			- 이름이 없을 경우 이름대신 이메일로 표시함.
			[2005-08-04] 이름대신 이메일을 표시하는 옵션추가.
		*/
		function get_mailto_url($email, $name=NULL, $is_view_email=false, $is_view_name=NULL, $opt=NULL)
		{
			global $G_SYS;
			#$target = 'target=_nm_hidden_frame';
			if ($name == NULL) $name = $email;

			if ($email)
			{
				// [2005-10-14] 웹메일은 회원전용이며 대부분의 자신의 메일목록만 관리하므로 인코딩 필요없음.
				#$return = "<a href='include/email.php?email=" . base64_encode($email) . "' " . $target . ">";
				// [2006-06-23] 메일주소 클릭시 이름도 같이 들어가도록 함.
				if ($name != '')
				{
					if ($is_view_name != '')
					{
						// [2009-08-07] 이름에 콤마(,)가 들어간 경우의 예외처리
						//$mailto = $is_view_name.' <'.$email.'>';
						$mailto = '"'.$is_view_name.'" <'.$email.'>';
					} else
					{
						// [2009-08-07] 이름에 콤마(,)가 들어간 경우의 예외처리
						//$mailto = $name.' <'.$email.'>';
						$mailto = '"'.$name.'" <'.$email.'>';
					}	// if()
				} else
				{
					$mailto = $email;
				}	// if()
				#$return = '<a href="'.$G_SYS[URL_ROOT].'/'.$G_SYS[MAILTO].urlencode($email).'" '.$target.'>';
				$return = '<a href="'.$G_SYS[URL_ROOT].'/'.$G_SYS[MAILTO].urlencode($mailto).'" '.$opt.'>';
				if (!$is_view_email)
				{
					$return .= $name;
				} else
				{
					$return .= $email;
				}	// if()
				$return .= "</a>";
			}else
			{
				$return = $name;
			}	// if()

			return $return;
		}	// function()


		/** Mail header 생성
			ex) $header = get_mail_header($mbr[m_email], $mbr[m_name], "HTML");

		*/
		function get_mail_header($email, $name=NULL, $type="HTML")
		{
			global $G_SYS;
			if ($name == NULL) $name = $email;

			$header = "From: $name<$email>\r\n";
			$header .= "MIME-Version : 1.0\r\n";
			$header .= "X-Sender: <$email>\n";
			$header .= "X-Mailer: PHP\n";
			$header .= "Return-Path: <$email>\n";  // Return path for errors

			switch($type)
			{
				case "HTML":
					$header .= "Content-Type: text/html; charset=".$G_SYS[CHARSET];
					break;

				case "TEXT":
					break;
			}	// switch()

			return $header;
		}	// function()


		/** 내용 html 처리
			- 게시판에서 사용시 등록한 형식(Y,M,N)에 맞게 내용을 처리함

			ex) $board[v_b_memo] = text_view($board[b_memo], $board[b_html]);

		*/
		function text_view($text, $type=NULL, $isadmin="N")
		{
			switch($type)
			{
				case "Y":		// HTML
					$value = ($isadmin == "Y") ? purifier_clean($text) : purifier_clean(del_tag($text));
					break;

				case "N":		// TEXT + <BR>
					$value = nl2br(htmlspecialchars_fix($text));
					break;

				case "M":		// HTML + <BR>
					$value = ($isadmin == "Y") ? nl2br(purifier_clean($text)) : nl2br(purifier_clean(del_tag($text)));
					break;

				case "REPLY":		// >> 내용
					// [2015-04] PHP5.4.x대응 대체함수 적용
					#$value = chr(10) . chr(10) . chr(10) . ">> " . ereg_replace(chr(10), chr(10).">> ", htmlspecialchars_fix($text));
					$value = chr(10) . chr(10) . chr(10) . ">> " . preg_replace("/".chr(10)."/", chr(10).">> ", htmlspecialchars_fix($text));
					break;

				default:		// TEXT
					$value = htmlspecialchars_fix($text);
					break;
			}	// switch()

			return $value;

		}	// function()




	// E-mail 주소가 올바른지 검사

		function isemail( $str ) {
			//if( eregi("([a-z0-9\_\-\.]+)@([a-z0-9\_\-\.]+)", $str) ) return $str;
			// [2004-04-21] -id-@hanmail.net 의 예외처리
			//if( eregi("([\-]*)([a-z0-9\_\-\.]+)([\-]*)@([a-z0-9\_\-\.]+)", $str) ) return $str;
			// [2004-05-27] ㅣ-id-@hanmail.net 등 주소앞에 한글이 들어간 경우의 예외처리
			// [2006-02-17] mail=ibin2.passkorea.net@sqlite.org
			// [2007-02-07] userid+test@gmail.com
			// [2009-03-16] girl\'s@sdaklfj.com
			//if (ereg("^[a-zA-Z0-9_\.\=\-]+@[a-zA-Z0-9\.-]+$", trim($str))) return true;
			//if (ereg("^[a-zA-Z0-9\+_\.\=\-]+@[a-zA-Z0-9\.-]+$", trim($str))) return true;
			$str = trim($str);
			// [2015-04] PHP5.4.x대응 대체함수 적용
			#if (     ereg("^[a-zA-Z0-9\!#\$%&'\*\+-/\=\?\^_`\.\{\|\}~]+@[a-zA-Z0-9\.-]+$", trim($str)))
			if (preg_match("/^[a-zA-Z0-9\!#\$%&'\*\+-\/\=\?\^_`\.\{\|\}~]+@[a-zA-Z0-9\.-]+$/", trim($str)))
			{
				$_ex = explode('@', $str);
				if (strpos($_ex[1], '..') === false && strpos($_ex[1], '.') !== 0 && substr($_ex[1], strlen($_ex[1])-1) != '.')
				{
					return true;
				}	// if()
			}	// if()
			return false;
		}

		// test
		/*
		$__emails = array(
			'-id-@hanmail.net',
			'mail=ibin2.passkorea.net@sqlite.org',
			'userid+test@gmail.com',
			'girl\'s@sdaklfj.com',
			'dsafads.D\'saffd@passkorea.net',

			'userid@.domain.com.',
			'userid@domain.com.',
			'userid@domain..com',
			'userid@domain..com.',
			'userid@.domain.com',
			);
		foreach ($__emails as $key=>$val)
		{
			echo "<HR>".$val; vd(isemail($val));
		}	// foreach()
		*/


	// 도메인 주소가 올바른지 검사

		function isdomain( $str ) {
			// [2009-11-30]
			// [2015-04] PHP5.4.x대응 대체함수 적용
			#if( ereg("^[a-zA-Z0-9\.-]+$", trim($str)) ) return true;
			if( preg_match("/^[a-zA-Z0-9\.\-\_]+$/s", trim($str)) ) return true;
			else return false;
		}


	// [2013-07-17] 올바른 아이디인지 검사
	function is_member_id( $str )
	{
		if ( preg_match("/^[a-z0-9\.\-\_]+$/", trim($str)) )
			return true;
		else
			return false;
	}	// function()


	// E-mail 의 MX를 검색하여 실제 존재하는 메일인지 검사

		function mail_mx_check($email) {
			if(!isemail($email)) return false;
			list($user, $host) = explode("@", $email);
			if (checkdnsrr($host, "MX") or checkdnsrr($host, "A")) return true;
			else $return = false;
		}



	/** URL이동 (meta tag)
	*/
	// [2007-01-18] 타겟 지정 가능하게 함.
		function movepage($src, $time=0, $target='') {
			// [2011-01-14] 커스터마이징용
			global $G_SYS, $nmpath;
			if (file_exists($G_SYS[PAGE_ROOT].'/include/global_user_define_before_end.inc.php'))
				include('include/global_user_define_before_end.inc.php');

			// [2005-08-30] src 가 없을때는 / 로 간주함.
			if (!$src)
				$src = '/';

			// [2026-07-07] 리다이렉션 보안 설정
			$is_allowed = is_redirect_url_check($src);

			if (! $is_allowed) {
				security_log('비정상 접근이 차단되었습니다. (외부 리다이렉션 차단)', 'url', $src);
			}

			// [2012-04-30] 모바일웹
			if ($_COOKIE['nm_mobile'])
			{
				if (is_string($src) && $src[0] != '/' && substr($src, 0, 2) != '..' && strpos($src, 'http://') !== 0 && strpos($src, 'https://') !== 0)
				{
					if (strpos($src, '/'.$G_SYS['MOBILE_PATH'].'/') === false)
					{
						// [2014-03-20] /mail/ 주소 사용시 오류 수정
						//$src = '/'.$G_SYS['MOBILE_PATH'].'/'.$src;
						$src = $nmpath.'/'.$G_SYS['MOBILE_PATH'].'/'.$src;
					}	// if()
				}	// if()
			}	// if()

			// [2010-05-27] WebKit 기반의 Chrome/Safari 웹브라우저에서 이동이 제대로 되지 않던 오류 수정.
			if (strpos($_SERVER['HTTP_USER_AGENT'], 'AppleWebKit') !== false)
			{
				if (is_string($src) && $src[0] != '/' && substr($src, 0, 2) != '..' && strpos($src, 'http://') !== 0 && strpos($src, 'https://') !== 0)
				{
					// [2013-07-17] Windows Server 일 경우, 예외 처리.
					//$_dir = dirname($_SERVER["PHP_SELF"]);
					$_dir = str_replace('\\', '/', dirname($_SERVER["PHP_SELF"]));
					if ($_dir == '/')
						$_dir = '';
					$src = $_dir.'/'.$src;
				}	// if()
			}	// if()

			if (!$target)
			{
				echo('<meta http-equiv="REFRESH" content="' . $time . '; url=' . e($src) . '">');
			} else
			{
				if (! preg_match('/^[a-zA-Z0-9\-_.]+$/', $target)) {
					security_log('비정상 접근이 차단되었습니다.', 'movepage() target', $target);
				}

				?>
				  <script type="text/javascript">
					  <!--
						<?= $target ?>.location.replace(<?= json_encode($src, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>);
					 // -->
				  </script>
			  <?
		  } // if()

			// [2025-09-22] 쿼리 디버깅
			if (isset($GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'])) {
				echo $GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'];
			}

			exit;
		}

		/**
		 * 리다이렉션 보안 설정에 따른 리다이렉트 허용 여부
		 *   - http/https URL은 허용 도메인 및 보안 정책에 따라 검사
		 *   - 허용할 도메인 직접 입력(REDIRECT_ALLOWED_URLS) 에 등록된 도메인은 우선 허용		 *
		 *   - 기본(DEFAULT) 정책: 메일 도메인 및 하위 서브도메인 허용
		 *   - 엄격(STRICT) 정책: 현재 웹메일 호스트만 허용
		 *
		 * @param string $src
		 *
		 * @return bool
		 */
		function is_redirect_url_check($src)
		{
			global $G_SYS, $AUTH;

			$is_allowed = true;
			$url = parse_url($src);

			// http, https가 붙는 경우에만 검사
			if (isset($url['scheme']) && in_array(strtolower($url['scheme']), array('http', 'https'))) {
				$is_allowed = false;
				$host = strtolower($url['host']);

				// REDIRECT_ALLOWED_URLS 설정값을 도메인 배열로 변환
				$allowed_url = array();
				if (isset($G_SYS['REDIRECT_ALLOWED_URLS'])) {
					if (strpos($G_SYS['REDIRECT_ALLOWED_URLS'], ',') !== false) {
						$allowed_url = explode(',', $G_SYS['REDIRECT_ALLOWED_URLS']);
					} else {
						$allowed_url[] = $G_SYS['REDIRECT_ALLOWED_URLS'];
					}
				}

				// 설정된 허용 도메인 우선 검사
				foreach ($allowed_url as $domain) {
					if ($host === strtolower(trim($domain))) {
						$is_allowed = true;
						break;
					}
				}

				// 허용 목록에 없는 경우 보안 정책 기준 검사
				if ( ! $is_allowed) {
					//
					$__http_url_root = 'http://' . $_SERVER['HTTP_HOST'];
					$__root_url = parse_url($__http_url_root);
					$__root_host = strtolower($__root_url['host']);

					if ($G_SYS['REDIRECT_SECURITY_POLICY'] == 'STRICT') {
						if ($host === $__root_host) {
							$is_allowed = true;
						}
					} else {
//						// 도메인 기준으로 서브도메인까지 허용
						$base_host = strtolower($G_SYS[MAIL_DOMAIN]);
						if (
							preg_match('/(^|\.)' . preg_quote($base_host, '/') . '$/', $host)
						) {
							$is_allowed = true;
						}
					}
				}
			}

			return $is_allowed;
		}


	// Message - Javascript
		/*
			설정에 따라 메시지만 보여줄수도 있고, 뒤로 돌아가게 할 수 도 있도록 함.
		*/
		function msg($message, $go=-1, $close=false) {
			global $db;

			// [2008-01-31] 메세지에 '가 들어가 있을 경우의 에러 방지.
			$message = str_replace("'", "", $message);

			if(!$close)		// 현재창
			{
				$go = abs($go);
				$js = "\n<SCRIPT language=\"javascript\">\n\twindow.alert('$message');";
				if ($go > 0) $js .= "\n\thistory.go(" . -($go) . ")";
				$js .= "\n</SCRIPT>";
				if ($go > 0)		// 뒤로 돌아갈때만 종료함.
				{
					unset_db();
					echo $js;

					// [2025-09-22] 쿼리 디버깅
					if (isset($GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'])) {
						echo $GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'];
					}

					exit;
				}else
				{
					echo $js;
				}	// if()
			}else		//  새창일때 닫기
			{
				$js = "\n<SCRIPT language=\"javascript\">\n\twindow.alert('$message');\n\tself.window.close();";
				$js .= "\n</SCRIPT>";

				unset_db();
				echo $js;

				// [2025-09-22] 쿼리 디버깅
				if (isset($GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'])) {
					echo $GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'];
				}

				exit;

			}	// if()

		}	// function()



	/** ////////////////////////////////////////////////////////////////////////////
	String To DB data

	1. [MySQL] [ ' ] 의 경우 PHP4 에서는 폼으로 넘길때 자동으로 슬래시를 덧붙여주므로 별도 처리가 필요없음.
	2. 지정된 길이만큼 잘라줌(한글지원)
	3. 태그제거

	[2005-08-04]
	-. 대부분 필드의 경우 HTML을 제한하므로 strip_tags 디폴트값을 'false'에서 'true'로 변경.
		ex)
			to_db_str($_POST['sg_name'], 50);
			to_db_str($_POST['sg_content'], $G_SYS['DB_TEXT'], false);

	[2006-01-27]
	-. addslashes() 후에 문자열을 자를때 마지막에 '\'가 붙어서 쿼리가 깨지는 오류 보완.
	[2006-02-04]
	-. '\'가 여러개 붙을 경우를 대비해 정규표현식으로 처리.
	*/

	function to_db_str($str, $substr=false, $strip_tags=true, $istrim=true)
	{
		// [2007-01-24] trim() 을 사용하지 않아아야할 곳 도 있음.
		if ($istrim)
			$str = trim($str);
		if ($strip_tags) $str = htmlspecialchars_fix($str);
		if ($substr)
		{
			// [2009-05-26] to_db_str()에서 호출된 경우에만 slashes 삭제/추가 처리함
			//$str = han_substr($str, $substr, true);
			$str = han_substr($str, $substr, true, true);
			// [2009-04-02] 일부 문자열에서 Segmentation fault 발생 오류 방지
			// $str = preg_replace("/(\\\)+$/", "", $str);
		}	// if()
		return $str;
	}		// function()

	/* ex)
		to_db_str(String, [hanSubstr_length], [strip_tags]);
		to_db_str($str, 50, "STRIP_TAGS");

		input) [하하] '슬\래\\시..'<B></B>' 넘ㅇ!@@#$@#$%#$^%..'"
		db__) [하하] \'슬\래\\시..'''' 넘ㅇ!@@#$@#$%#$^%..''&quot;

	*/


	/** ////////////////////////////////////////////////////////////////////////////
	한글 완벽하게 자르기
		- han_substr($string, $limit_length, "noPoint");  로 사용할경우 반환값 뒤에 "..."을 붙이지 않도록함.
		- 세번째 인수인 "noPoint" 자체 생략가능

	*/
	function han_substr($string, $limit_length, $isNoPoint=false, $is_slashes=false, $is_strimwidth=false)
	{
		global $G_SYS;

		// $isNoPoint=TRUE - 문자열을 자른뒤 뒤 "..."를 붙일지 않음.
		// [2009-05-12] 마지막 문자열이 \로 잘리면서 발생하는 구문 오류 방지.
		// [2009-05-26] to_db_str()에서 호출된 경우에만 slashes 삭제/추가 처리함
		// [2013-07-04] UTF-8 버전을 위해 mb_strcut() 으로 변경
		if ($is_slashes)
			$string = stripslashes($string);

		if (strtoupper($G_SYS[CHARSET]) == 'EUC-KR')
		{
			$encoding = 'CP949';
		} else
		{
			$encoding = $G_SYS[CHARSET];
		}	// if()

		if ($is_strimwidth)		// 화면상에 보여지는 글자 사이즈로 자르기
		{
			$string_return = mb_strimwidth($string, 0, $limit_length, NULL, $encoding);
		} else		// 실제 DB에 저장되는 글자 길이(byte)로 자르기
		{
			$string_return = mb_strcut($string, 0, $limit_length, $encoding);
		}	// if()

		if (!$isNoPoint && strlen($string_return) != strlen($string))		// 문자열이 잘렸고, 마침표를 뒤에 붙여야 하는 경우.
			$string_return .= '...';

		if ($is_slashes)
			$string_return = addslashes($string_return);
		return $string_return;
	}	// function

	function han_substr_view($string, $limit_length)
	{
		global $G_SYS;

		return han_substr($string, $limit_length, false, false, true);
	}	// function


	### [String] 올바른 형식의 URL인지 확인
		function urlCheck($url)
		{
			// [2015-04] PHP5.4.x대응 대체함수 적용
			#if(ereg("([^[:space:]]+)", $url) && (!ereg("http://([0-9a-zA-Z./@~?&=_]+)", $url)) )
			if(preg_match("/([^[:space:]]+)/", $url) && (!preg_match("/http:\/\/([0-9a-zA-Z.\/@~?&=_]+)/", $url)) )
			{
				return false;
			}else
			{
				return true;
			}
		}

	### [Text] 검색된 단어를 강조시켜서 출력 ###
		function searchWordColor($searchword, $text, $color)
		{
			if ( (strlen($searchword) > 0) && (strlen($text) > 0) )		// 값이 존재할때만 변환함.
			{
				// [2015-04] PHP5.4.x대응 대체함수 적용
				#$text = eregi_replace($searchword, "<FONT color=$color>$searchword</FONT>", $text);		// 대소문자 무시함.
				$text = preg_replace("/".$searchword."/i", "<FONT color=$color>$searchword</FONT>", $text);		// 대소문자 무시함.
			}	// if()

			return $text;
		}	// function


	### [Form] input Value 채워넣기 -  ###
		// 2001/03/28/WED -차계부-
		// 값이 존재할때만 출력함. 큰따옴표로 인한 값 잘림방지

		function input_value($value, $return=0)
		{
			// [2015-04] PHP5.4.x대응 대체함수 적용
			#$value = ereg_replace(chr(34), "&#34;", $value);		// 큰따옴표(")일때 VALUE값이 잘리는것을 방지하기위해 아스키코드로 변경함.
			$value = preg_replace("/".chr(34)."/", "&#34;", $value);		// 큰따옴표(")일때 VALUE값이 잘리는것을 방지하기위해 아스키코드로 변경함.
			if($value != null) $value = "VALUE=\"$value\"";		// SUBMIT()후 에러가 나서 HISTORY.BACK()할때 value값을 사용자가 입력한채로 유지하기 위함.
			if ($return)
			{
				return($value);
			}else
			{
				echo($value);
			}	// if()
		}


	### [Form] Checkbox 채워넣기 및 선택하기 ###
		// 2001/05/29/TUE

		function checkbox_value($objName, $objCheckedArray, $objTextArray, $objValueArray, $objArrayUseNo)
		{
			$Gy_return;

			// Checkbox Add&Selected Process
				for ($i=$objArrayUseNo; $i<sizeof($objTextArray); $i++)
				{
					$isChecked = "N";
					for ($j=0; $j<sizeof($objCheckedArray); $j++)
					{
						if ($objValueArray[$i] == $objCheckedArray[$j])
						{
							$isChecked = "Y";
							break;
						}
					}	// for()

					// [2007-12-12]
					$objId = str_replace('[]', '', $objName)."_".$i;
					$Gy_return[$i] = '<INPUT TYPE=checkbox NAME="' . $objName . '" id="' . $objId . '" VALUE="' . $objValueArray[$i] . '"';
					if ( $isChecked == "Y" )		// 선택된 항목일경우 체크함
						$Gy_return[$i] .= ' checked';
					$Gy_return[$i] .= '><LABEL for=' . $objId . '>'.$objTextArray[$i].'</LABEL>';
				}	// for()

			return($Gy_return);

		}	// function()


	### [Form] Radio 단추 채워넣기 및 선택하기 ###
		// 2001/05/29/TUE

		function radio_value($objName, $objValue, $objTextArray, $objValueArray, $objDefaultNo, $objArrayUseNo, $return=0, $option=NULL, $is_htmlspecialchars=true)
		{

			// radio Add&Selected Process
				$value = NULL;
				for ($i=$objArrayUseNo; $i<sizeof($objTextArray); $i++)
				{
					if ( ( $objValueArray[$i] == $objValue ) || ( (string)$i == (string)$objDefaultNo ) )		// 선택된 항목일경우 체크함
					{
						$ischecked = true;
					} else {
						$ischecked = false;
					}	// if()

					// [2007-12-12]
					$objId = str_replace('[]', '', $objName)."_".$i;
					$value .= '<INPUT TYPE=radio class=inputbutton NAME="' . $objName . '" id="' . $objId . '" VALUE="' . $objValueArray[$i] . '"';
					if ($ischecked)
						$value .= ' checked';
					if ($is_htmlspecialchars)
					{
						$value .= '><LABEL for=' . $objId . '>'.htmlspecialchars_fix($objTextArray[$i]).'</LABEL>';
					} else
					{
						$value .= '><LABEL for=' . $objId . '>'.$objTextArray[$i].'</LABEL>';
					}	// if()
					$value .= ' ';
				}	// for()

			// return
				if ($return)
				{
					return($value);
				}else
				{
					echo($value);
				}	// if()

		}	// function()


	### [Form] SELECT Option 채워넣기 및 선택하기  ###
		//  Option Add&Selected (선택될 전달값, 객채의 문자열 배열, 객채의 전달값 배열, 객채의 첫번째 문자열[안내문구], 배열중 사용될 시작번호)

		function select_list_value ($objValue, $objTextArray, $objValueArray="ValueIsNumber", $objTextFirst="선택하세요", $objArrayUseNo=0, $return=0) {
			$value = "";

			// 배열시작번호가 없을경우 0
				if ( ! $objArrayUseNo ) $objArrayUseNo=0;

			// 안내문구가 있을때만 출력함.
				if ( $objTextFirst )
				{
					$value .= "\n<OPTION VALUE=\"\">$objTextFirst</OPTION>";
					$value .= "\n<OPTION VALUE=\"\">";		for ($i=0; $i<strlen($objTextFirst); $i++) {$value .= "-";}		$value .= "</OPTION>";
				}

			// 객채의 전달값 배열이 0부터시작되는 숫자일때 숫자배열 자동생성해줌.
				if ( $objValueArray == "ValueIsNumber" )
				{
					$objValueArray = "";
					for ($i=0; $i<sizeof($objTextArray); $i++)
					{
						$objValueArray[$i] = $i;
					}
				}

			// Option Add&Selected Process
				for ($i=$objArrayUseNo; $i<sizeof($objTextArray); $i++) {
					//if ( $objValueArray[$i] != $objValue ) {
					if ( strcmp($objValueArray[$i], $objValue) ) {		// 0, "" 과 null 값을 구분해냄.
						$value .= "\n<OPTION VALUE=\"" . htmlspecialchars_fix($objValueArray[$i]) . "\">$objTextArray[$i]</OPTION>";
					} else {
						$value .= "\n<OPTION VALUE=\"" . htmlspecialchars_fix($objValueArray[$i]) . "\" selected>$objTextArray[$i]</OPTION>";		// 선택된 항목일경우 체크함
					}
				}

			// view
				if ($return)
				{
					return($value);
				}else
				{
					echo($value);
				}	// if()

		}	// function()


	### [String] URL 자동링크 ###
		function auto_link($str)
		{
			// [2025-11-04] <url> 형식에서 HTML 엔티티 &gt; 로 끝나는 경우 예외 처리
			$str = str_replace("&gt;", "\n>\n", $str);
			$str =  preg_replace(
					"/(http|https|ftp|mms)(:\/\/[^ \n\r\t<>]+)/i",
					"<a href=\"\\1\\2\" target=\"_blank\" rel=\"noreferrer noopener\">\\1\\2</a>",
					$str
			);
			return str_replace("\n>\n", "&gt;", $str);
		}	// function()


	### [Tag] 특정 HTML 제거 - 화면깨짐방지 ###
		function del_tag($text) {
		  //$text = eregi_replace("<html(.*)<body([^>]*)>","",$text);
		  //$text = eregi_replace("</body(.*)</html>","",$text);
		  //$text = eregi_replace("<(\/)*(table|tr|td|body|html|head|meta|form|input|select|div|span|layer|textarea)[^>]*>","",$text);
		  //$text = eregi_replace("<(style|script|title)(.*)</(style|script|title)>","",$text);
		  //$text = eregi_replace("<[/]*(script|style|title)>","",$text);
		  //$text = preg_replace('/<!--(.*?)-->/is',"",$text);
		  //$text = preg_replace('/\/\*(.*?)\*\//is', '', $text);
		  $text = preg_replace("!<html(.*)<body([^>]*)>!is","",$text);
		  $text = preg_replace("!<\/body(.*)<\/html>!is","",$text);
		  $text = preg_replace("!<(\/)*(table|tr|td|body|html|head|meta|form|input|select|div|span|layer|textarea|link|base)[^>]*>!is","",$text);
		  $text = preg_replace("!<(style|script|title)(.*)<\/(style|script|title)>!is","",$text);
		  $text = preg_replace("!<\/*(script|style|title)>!is","",$text);
		  $text = trim($text);

		  return $text;
		}

    ### [2020-02] KVE SQL Injection 개선
    ### [2020-11] SQL Injection 개선
        function del_injection_str($str, $default_str='')
        {
            global $db;

            $patterns = array(
                "/<script(.*?)<\/script>/is",
            );
            $replacements = array(
                "",
            );

            $strings = is_array($str) ? $str : array($str);
            foreach ($strings as $key => $string)
            {
	            $string = preg_replace($patterns, $replacements, $string);

	            //$str = preg_replace("/[\r\n\s\t\'\;\"\=\-\-\#\/*]+/",'', $str);
	            $string = preg_replace("/[\r\n\t\/*]+/", '', $string);
	            //$str = preg_replace("/(union|select|from|where)/i",'', $str);
	            if (preg_match("/(select\s|delete\s|update\s)/i", $string)
		            && preg_match(
			            "/(\sfrom|\swhere\s|\sand\s|\sor\s|\slike\s|\sunion\s|union\sall|\slimit\s|\shaving\s|\sset\s|group\sby|;)/i",
			            $string
		            )) {
		            $string = $default_str;
	            }

	            $strings[$key] = $string;
            }

            if (is_array($str))
            {
	            return $strings;
            } else
            {
	            return $strings[0];
            }
        }

/**
 * JSON 응답
 *
 * @param mixed $data
 *
 * @return string
 */
function response_json($data)
{
	header('Content-Type: application/json');

	return json_encode($data);
}

/**
 * DB 데이터 바인딩전 처리
 *
 * @param array $data
 *
 * @return array
 */
function db_binding_prefilter($data)
{
	global $G_SYS;

	$count = count($data);
	for ($i = 0; $i < $count; $i++) {
		// [2014-05-22] mysql 에서도 prepare 구문 지원.
		if ($G_SYS[DB] == 'oracle') {
			// [2006-03-10] oracle 에서 메일 데이타 저장시에만 쿼터가 붙던 오류 해결.
			$data[$i] = db_query_quote($data[$i], true);
		} else {
			// [2025-11-05] 문자열일 경우만 처리하여, db_create() 등에서 바인딩시 null 등 문자열 유지
			if (is_string($data[$i])) {
				$data[$i] = stripcslashes($data[$i]);
			}
		}
	}    // for()

	return $data;
}

/**
 * 디버깅 출력
 *
 * @param        $var
 * @param string $title
 */
function dd($var, $title = '')
{
	if ( ! empty($title)) {
		echo "<h1>dd: " . $title . "</h1>";
	}
	if (extension_loaded('xdebug')) {
		ini_set("xdebug.var_display_max_children", '-1');
		ini_set("xdebug.var_display_max_data", '-1');
		ini_set("xdebug.var_display_max_depth", '-1');
		var_dump($var);
	} else {
		echo "<xmp>";
		var_dump($var);
		echo "</xmp>";
	}

	// [2025-09-30] 로그 디버깅
	if (isset($GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'])) {
		echo $GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'];
	}

	exit;
}

/**
 * HTML 태그를 일반 문자열로 표시
 *   - XSS 방어
 *
 * @param string $value
 *
 * @return string
 */
function e($value)
{
	return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
}

/**
 * javascript 함수 파라미터에서 작은 따옴표(')에 슬래시 추가
 *
 * @param string $value
 *
 * @return string
 */
function e_js_param($value)
{
	return str_replace(array("\\", "'"), array("\\\\", "\\'"), $value);
}

/**
 * 로컬 개발 환경 여부
 *
 * @return bool
 */
function is_local()
{
	return (
			(
					isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === '127.0.0.1'
					|| PHP_SAPI === 'cli'
			)
			&& file_exists(
					__DIR__ . '/../../app_env_local'
			));
}

/**
 * 키 배열을 문자열로 합치기
 *    ex) array(1, 2) -> '1', '2'
 *
 * @param array $array
 * @param string $separator
 *
 * @return string
 */
function implode_keys($array, $separator = ",") {
	if (!is_array($array) || count($array) == 0) {
		return '';
	}

	foreach ($array as $key => $val) {
		if (! preg_match('/^[a-zA-Z0-9\-_.]+$/', $val)) {
			security_log('비정상 접근이 차단되었습니다.', 'implode_keys()', $val);
		}
	}

	return "'" . implode("'" . $separator . "'", $array) . "'";
}

/**
 * 보안 차단 로그 기록 및 실행 중단
 *
 * @param string $message
 * @param string $key
 * @param string $val
 *
 * @return void
 */
function security_log($message, $key, $val)
{
	global $G_SYS;

	// 사용자 정의 예외 처리
//	if ($_SERVER['PHP_SELF'] === '/index_custom.php' && $key === 'exclude_key') {
//		return ture;
//	}

	// security-날짜.txt 로 차단 로그 남기기
	errorlog($message . ' | ' . $key . ' | ' . $val, 'SECURITY');

//	$message_print = "Access Denied (" . $key . ")";
	$message_print = $message . " [" . $key . "]";

	// 로컬 개발 환경에서만 입력값 표시
	if (is_local()) {
		$message_print .= ' ' . e(var_export($val, true));
	}

	// 실행 중단
	header('Content-Type: text/html; charset=' . $G_SYS[CHARSET]);
	die($message_print);
}

/**
 * Thread Safety 정보
 *
 * @return string
 */
function get_php_ini_thread_safety()
{
    global $crlf, $php_info;
    if ($php_info == '')
    {
        ob_start();
        phpinfo(INFO_GENERAL);
        $php_info = ob_get_contents();
        ob_end_clean();
    }	// if()

    foreach (explode("\n",$php_info) as $line) {
        // [2012-10-23]
        //if (eregi("Configure Command.*(</B></td><TD ALIGN=\"left\">| => |v\">)([^<]*)(.*</td.*)?",$line,$match)) {
        if (preg_match("/Thread Safety[ =>]*([^<]+)/i",strip_tags($line),$match)) {
            $thread_safety = $match[1];
        }
    }

//    if (strtolower(trim($thread_safety)) == 'enabled')
//    {
//        $thread_safety = true;
//    } else
//    {
//        $thread_safety = false;
//    }	// if()

    return $thread_safety;
}	// function()

/**
 * architecture 정보
 *
 * @return string
 */
function get_php_ini_architecture ()
{
	return (PHP_INT_SIZE === 4) ? '32bit' : '64bit';
}	// function()

/**
 * 메일 검색 쿼리
 *
 * @return array
 *               - string $q_where
 *               - array $bindings
 *               - string|null $v_s_word
 *               - array $vs
 */
function mail_list_search_query()
{
	global $G_SYS, $AUTH;

	$bindings = array();
	$q_where = '';
	$v_s_word = null;
	$vs = array();
	if ($_GET['sd'] == '1')
	{
		$s_key = array('s_from', 's_to', 's_cc', 's_subject', 's_body', 's_attach');
		$s_filed = array('mf_from', 'mf_to', 'mf_cc', 'mf_subject', 'mf_body', 'mf_attach_names');
		for ($i=0; $i<sizeof($s_key); $i++)
		{
			if (trim($_GET[$s_key[$i]]) != '')
			{
				set_q_search($_GET[$s_key[$i]]);

				if ($G_SYS[DB] == 'oracle')
				{
					if ($s_filed[$i] == 'mf_body')
					{
						$q_where .= " AND dbms_lob.instr(mf_body, ?)>0 ";
						$bindings[] = $_GET[$s_key[$i]];
					} else
					{
						$q_where .= " AND ".$s_filed[$i]." LIKE ? ";
						$bindings[] = '%' . $_GET[$s_key[$i]] . '%';
					}	// if()
				} else
				{
//						$q_where .= " AND ".$s_filed[$i]." LIKE '%" . del_injection_str($_GET[$s_key[$i]]) . "%' ";
					$q_where .= " AND ".$s_filed[$i]." LIKE ? ";
					$bindings[] = '%' . $_GET[$s_key[$i]] . '%';
				}	// if()

				$vs[$s_key[$i]] = htmlspecialchars_fix(stripslashes($_GET[$s_key[$i]]));
			}	// if()
		}	// for()

		if ($_GET['sd1'] && strtotime($_GET['sd1']))
		{
			$_ex = explode('-', $_GET['sd1']);
			$_GET['sd1'] = sprintf('%04d', $_ex[0]).'-'.sprintf('%02d', $_ex[1]).'-'.sprintf('%02d', $_ex[2]);

			if ($_GET['sd2'] && strtotime($_GET['sd2']))
			{
				$_ex = explode('-', $_GET['sd2']);
				$_GET['sd2'] = sprintf('%04d', $_ex[0]).'-'.sprintf('%02d', $_ex[1]).'-'.sprintf('%02d', $_ex[2]);

				$q_where .= " AND mf_arrival_date >= '".del_injection_str($_GET['sd1'])." 00:00:00' AND mf_arrival_date <= '".del_injection_str($_GET['sd2'])." 23:59:59' ";
			}	// if()
		}	// if()

		$vs['basic_display'] = 'none';
		$vs['detail_display'] = '';

	} else
	{
//            $_GET['s_word'] = del_injection_str($_GET['s_word']);
		if ($_GET['s_word'])
		{
			set_q_search($_GET['s_word']);
			/*
			if (strpos($_GET['s_word'], ' ') !== false)
			{
				$q_where .= " AND ( 0 ";
				$_ex = explode(' ', $_GET['s_word']);
				for ($i=0; $i<sizeof($_ex); $i++)
				{
					$_word = trim($_ex[$i]);
					if (strlen($_word) >= 2)
					{
						$q_where .= " OR mf_subject LIKE '%" . $_word . "%' ";
						$q_where .= " OR mf_from LIKE '%" . $_word . "%' ";
						$q_where .= " OR mf_body LIKE '%" . $_word . "%' ";
					}	// if()
				}	// for()
				$q_where .= " ) ";
			} else
			{
			*/
			// 제목, 보낸사람, 본문 검색
			// [2006-02-28] 8i 지원
			// [2012-05-10] 보낸편지함/임시보관함에서는 '보낸사람'대신 '받는사람'을 검색하도록 함.
			if ($_GET['mb_id'] == 'sent' || $_GET['mb_id'] == 'draft')
			{
				$q_from_to = " OR mf_to LIKE ?";
				$bindings[] = '%' . $_GET['s_word'] . '%';
			} else if ($_GET['mb_id'])
			{
				$q_from_to = " OR mf_from LIKE ?";
				$bindings[] = '%' . $_GET['s_word'] . '%';
			} else
			{
				$q_from_to = " OR mf_to LIKE ? OR mf_from LIKE ?";
				$bindings[] = '%' . $_GET['s_word'] . '%';
				$bindings[] = '%' . $_GET['s_word'] . '%';
			}	// if()

			// [2022-11-25] '참조' 검색 추가
			$q_from_to .= " OR mf_cc LIKE ?";
			$bindings[] = '%' . $_GET['s_word'] . '%';

			if ($G_SYS[DB] == 'oracle')
			{
				$q_where = " AND ( mf_subject LIKE ?".$q_from_to." OR  dbms_lob.instr(mf_body, ?)>0 ) ";
				$bindings[] = '%' . $_GET['s_word'] . '%';
				// [2024-09-24] Oracle 바인딩 재지정
				$bindings[] = $_GET['s_word'];
			} else
			{
				$q_where = " AND ( mf_subject LIKE ?".$q_from_to." OR mf_body LIKE ? ) ";
				$bindings[] = '%' . $_GET['s_word'] . '%';
				$bindings[] = '%' . $_GET['s_word'] . '%';
			}	// if()

			#$q_where .= " AND ( mf_subject LIKE '%" . $_GET['s_word'] . "%' ) ";
			#}	// if()
			// 제목, 보낸사람 검색
			#$q_where = " AND ( mf_subject LIKE '%" . $_GET['s_word'] . "%' OR mf_from LIKE '%" . $_GET['s_word'] . "%' ) ";
			$v_s_word = htmlspecialchars_fix(stripslashes($_GET['s_word']));


			// [2012-03-06]기본 검색에서 검색기간 조건 추가
			if (is_numeric($_GET['s_date']) && $_GET['s_date'] > 0)
			{
				$_time = time();
				$_sd1 = date('Y-m-d', $_time-(86400*$_GET['s_date']));
				$_sd2 = date('Y-m-d', $_time);
				$q_where .= " AND mf_arrival_date >= '".$_sd1." 00:00:00' AND mf_arrival_date <= '".$_sd2." 23:59:59' ";
			}	// if()
		}	// if()

		$vs['basic_display'] = '';
		$vs['detail_display'] = 'none';

	}	// if()
	if ($_GET['s_noread'])
	{
		$q_where .= " AND mf_is_read = '". del_injection_str($_GET['s_noread']) . "'";
	}
	$mb_child_count = 0;
	if ($_GET['mb_id']) {
		// 현재 편지함의 자식수 가져오기
		$mb_child_count = get_mail_box_info($_GET['mb_id'], 'mb_child_count');

		if ((int)$mb_child_count > 0) {
			if ($_GET['include_child'] === '1') {
				$mail_box_tree_class = get_mail_box_tree_class();

				$row = $mail_box_tree_class->getRowByKey($_GET['mb_id'], true, " AND " . get_mail_box_where());

				$child_mail_box = $mail_box_tree_class->getRowsByParent(
						$row['mb_id'], 255, $row['mb_depth'] + 1, "AND " . get_mail_box_where()
				);

				$array_mb_id = array($_GET['mb_id']);
				for ($i = 0; $i < count($child_mail_box['mb_id']); $i++) {
					$array_mb_id[] = $child_mail_box['mb_id'][$i];
				}

				$q_where .= " AND mb_id IN (" . implode_keys($array_mb_id) . ") ";
			} else {
				$q_where .= " AND mb_id = '" . del_injection_str($_GET['mb_id']) . "'";
			}
		} else {
			$q_where .= " AND mb_id = '" . del_injection_str($_GET['mb_id']) . "'";
		}
	} else {
			// [2023-11-20] 전체메일에서 스팸편지함, 지운편지함, 임시보관함 제외
			$q_where .= " AND mb_id NOT IN (" . implode_keys($G_SYS['NM_ALL_MAIL_EXCLUDE']) . ")";
	}

	if ($_GET['s_star'])
	{
		$q_where .= " AND ms.id is not null";
	}

	return array($q_where, $bindings, $v_s_word, $vs, $mb_child_count);
}

function favicon_view()
{
	global $G;

	$ex_file = explode(".", $G['DINFO']['d_site_favicon']);
	$ext = $ex_file[1];
	require_once('include/file.php');
	$file_url = file_down_url($G['DINFO']['d_site_favicon'], '1', 'favicon/'.$G['DINFO']['d_name']);

	switch ($ext) {
		case 'png':
			$type = 'image/png';
			break;

		case 'svg':
			$type = 'image/svg+xml';
			break;

		case 'webp':
			$type = 'image/webp';
			break;

		case 'gif':
			$type = 'image/gif';
			break;

		case 'ico' :
		default:
			$type = 'image/x-icon';
			break;
	}

	return "<link rel=\"shortcut icon\" type=\"". $type . "\" href=\"" . $file_url . "\">";
}

/**
 * 에디터 임시 이미지 주소를 실제 이미지 주소로 변경
 *
 * @param string $content   에디터 본문
 * @param string $tmp_url   임시 이미지 주소
 * @param string $url       실제 이미지 주소
 *
 * @return string
 */
function editor_content_cid_replace($content, $tmp_url, $url)
{
	global $G_SYS;

	$_cid_tmp_url = $G_SYS['HTTP_URL_ROOT'] . '/' . $tmp_url;
	$_cid_url = $G_SYS['HTTP_URL_ROOT']. '/' . $url;
	// <img src=\"http://pc.mail.passkorea.net/board_image_tmp.php?cid=1758176985.7311&amp;name=mceu_42454805411758176985690.gif\" />
//					$pattern = "/src=\"".preg_quote($_cid_tmp_url, '/')."([a-zA-Z0-9\.]+)/i";
	$pattern = "/".preg_quote("src=\\\"" . $_cid_tmp_url, '/')."([a-zA-Z0-9\.]+)(".preg_quote('&amp;').")/i";

	return preg_replace_callback($pattern, function ($matches) use ($_cid_url) {
		$data = "src=\\\"" . $_cid_url . $matches[1];
		if (isset($matches[2]) && $matches[2] === '&amp;')  {
			$data .= "&";
		}

		return $data;
	}, $content);
}

/**
 * 에디터 이미지 파일명 등 추출
 *   - 본문내 임시 이미지 주소에서 크기 및 파일명 추출
 *   - 수정시 본문내 이미지 주소가 없을 경우, 업로드된 이미지 삭제
 *
 * @param string      $content          에디터 본문
 * @param string      $url              이미지 주소 (등록시 임시 주소, 수정시 업로드된 주소)
 * @param string      $original_files   에디터 업로드시 폼에서 저장된 값.   예시) mceu_62398038911758616104651.gif|1758616104.6872|1592   2개 이상일 경우 || 로 구분
 * @param string      $type             업로드 디렉토리 구분.  board, sign
 * @param string|null $no               DB Primary Key
 * @param bool        $is_create        등록/수정에 따른 임시 이미지 주소 차이 분기
 * @param string|null $mode             게시물 답변시 분기 등
 *
 * @return array
 */
function editor_image_files($content, $url, $original_files, $type, $no = null, $is_create = false, $mode = null)
{
	global $G_SYS, $AUTH;

	$size = 0;
	$file_names = array();
	$_cid = array();
	$_cid_url = $G_SYS['HTTP_URL_ROOT']. '/' . $url;

	if ($is_create) {
		// 등록 - http://pc.mail.passkorea.net/sign_image_tmp.php?cid=1758615679.9864&amp;name=mceu_95321479311758615679961.gif
		$pattern = "/".preg_quote("src=\\\"" . $_cid_url, '/')."([a-zA-Z0-9\.]+)(".preg_quote('&amp;').")/i";

		if (preg_match_all($pattern, $content, $matches)) {
			$_cid = $matches[1];
		}
	} else {
		// 수정 - http://pc.mail.passkorea.net/sign_image.php?sg_no=6&amp;cid=1758039650.9483&amp;name=nmail.gif
		$pattern = "/" . preg_quote("src=\\\"" . $_cid_url, '/')
				. "(&|" . preg_quote('&amp;') . ")+cid\="
				. "([a-zA-Z0-9\.]+)/i";

		if (preg_match_all($pattern, $content, $matches)) {
			$_cid = $matches[2];
		}
	}
//	var_dump($is_create, $pattern, $content, $matches);

	// ㅂ 매칭되면 b_image_size 다시 구하기
	if ( ! empty($_cid)) {
		$_ex = explode('||', $original_files);
		for ($i = 0; $i < sizeof($_ex); $i++) {
			$files = explode('|', $_ex[$i]);
			if (in_array($files[1], $_cid)) {
				$size += (int)$files[2];
			}
			$file_names[] = $files[1];
		}
	}

	// 수정시 본문내 이미지 주소가 없을 경우, 업로드된 이미지 삭제
	if ($mode === null && $no !== null) {
		if ($type === 'sign') {
			$upload_dir = $type . '/' . $G_SYS[MAIL_DOMAIN] . '/' . $AUTH[auth_m_mail_file_group] . '/'
					. $AUTH[auth_m_id] . '/' . $no;
		} else {
			$upload_dir = $type . '/' . $G_SYS[MAIL_DOMAIN] . '/' . $no;
		}

		if ( ! empty($_cid)) {
			// array_diff는 배열키 유지해서 0부터 다시 매기기 위해 array_values 추가
			$deletes = array_values(array_diff($file_names, $_cid));

			for ($i = 0; $i < sizeof($deletes); $i++) {
				file_del($deletes[$i], '', $upload_dir);
			}
		} else {
			$_ex        = explode('||', $original_files);
			for ($i = 0; $i < sizeof($_ex); $i++) {
				$files = explode('|', $_ex[$i]);
				file_del($files[1], '', $upload_dir);
			}
		}
	}

	// DB에 저장할 파일명 구조 생성 ($original_files 과 같은 형식)
	$change_files = array();
	for ($i = 0; $i < sizeof($_ex); $i++) {
		$files = explode('|', $_ex[$i]);
		if (in_array($files[1], $_cid)) {
			$change_files[] = $_ex[$i];
		}
	}
	$change_files = implode('||', $change_files);

	return array($size, $change_files);
}

// PHP 5.4 이전 지원
// https://www.php.net/manual/en/function.http-response-code.php
if (!function_exists('http_response_code')) {
	function http_response_code($code = NULL) {

		if ($code !== NULL) {

			switch ($code) {
				case 100: $text = 'Continue'; break;
				case 101: $text = 'Switching Protocols'; break;
				case 200: $text = 'OK'; break;
				case 201: $text = 'Created'; break;
				case 202: $text = 'Accepted'; break;
				case 203: $text = 'Non-Authoritative Information'; break;
				case 204: $text = 'No Content'; break;
				case 205: $text = 'Reset Content'; break;
				case 206: $text = 'Partial Content'; break;
				case 300: $text = 'Multiple Choices'; break;
				case 301: $text = 'Moved Permanently'; break;
				case 302: $text = 'Moved Temporarily'; break;
				case 303: $text = 'See Other'; break;
				case 304: $text = 'Not Modified'; break;
				case 305: $text = 'Use Proxy'; break;
				case 400: $text = 'Bad Request'; break;
				case 401: $text = 'Unauthorized'; break;
				case 402: $text = 'Payment Required'; break;
				case 403: $text = 'Forbidden'; break;
				case 404: $text = 'Not Found'; break;
				case 405: $text = 'Method Not Allowed'; break;
				case 406: $text = 'Not Acceptable'; break;
				case 407: $text = 'Proxy Authentication Required'; break;
				case 408: $text = 'Request Time-out'; break;
				case 409: $text = 'Conflict'; break;
				case 410: $text = 'Gone'; break;
				case 411: $text = 'Length Required'; break;
				case 412: $text = 'Precondition Failed'; break;
				case 413: $text = 'Request Entity Too Large'; break;
				case 414: $text = 'Request-URI Too Large'; break;
				case 415: $text = 'Unsupported Media Type'; break;
				case 500: $text = 'Internal Server Error'; break;
				case 501: $text = 'Not Implemented'; break;
				case 502: $text = 'Bad Gateway'; break;
				case 503: $text = 'Service Unavailable'; break;
				case 504: $text = 'Gateway Time-out'; break;
				case 505: $text = 'HTTP Version not supported'; break;
				default:
					exit('Unknown http status code "' . htmlentities($code) . '"');
					break;
			}

			$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');

			header($protocol . ' ' . $code . ' ' . $text);

			$GLOBALS['http_response_code'] = $code;

		} else {

			$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);

		}

		return $code;

	}
}

/**
 * 쿼리 디버깅
 *   - 로컬 개발 환경에서만 표시
 *
 * @param string $function
 * @param float  $_exec_time
 * @param string $query
 * @param array  $bindings
 */
function db_debug($function, $_exec_time, $query, $bindings = array())
{
	if (is_local()) {
		$query      = trim($query);
		$lines      = explode("\n", $query);
		$query_view = '';
		foreach ($lines as $line) {
			$query_view .= trim($line) . ' ';
		}
		$query_view = trim(
				str_replace(
						array("\r\n", "\n"),
						array(' ', ' '),
						$query_view
				)
		);

		$log_level = 'debug';
		if ($function === 'db_query' && stripos($query, 'SELECT') !== 0 && stripos($query, 'SET') !== 0) {
			$log_level = 'warn';
		}

		$message = $function . ' (' . round(
						$_exec_time,
						4
				) . 'sec) : ' . $query_view;

		if (count($bindings) > 0) {
			$message .= ';' . PHP_EOL . str_repeat(' ', 25) . db_query_log($query_view, $bindings);
		}

		$GLOBALS['__NMAIL_DEBUG_CONSOLE_LOG'] .= '<script>console.' . $log_level . '('
				. json_encode(
						$message,
						version_compare(PHP_VERSION, '5.4.0', '>=')
								? JSON_UNESCAPED_UNICODE
								: null
				)
				. ');</script>' . PHP_EOL;
	}
}

/**
 * 쿼리문과 바인딩을 로그 형식으로 변환
 *
 * @param string     $query
 * @param array|null $bindings
 *
 * @return string
 */
function db_query_log($query, $bindings = array())
{
	if ( ! empty($bindings)) {
		// 문자열일 경우 작은 따옴표(')로 둘러쌈
		foreach ($bindings as $key => $binding) {
			if (is_int($binding) || is_float($binding)) {
				$bindings[$key] = $binding;
			} else {
				if ($binding === null) {
					$bindings[$key] = "NULL";
				} else {
					$bindings[$key] = "'" . str_replace("'", "\'", $binding) . "'";
				}
			}
		}

		// 쿼리문에 입력값을 바인딩하여 로그로 남김.
		$query = str_replace(array('%', '?'), array('%%', '%s'), $query);
		$query = vsprintf($query, $bindings);
	}

	return $query;
}

/**
 * DB 데이타 삽입
 *   - 대량 할당 지원: 키->밸류 배열로 값을 전달하면 insert 쿼리문이 자동 생성되어 실행됨
 *
 * @param string $table
 * @param array  $attributes    주의) 데이터는 $_POST, $_GET 처럼 addslashes() 적용된 상태여야 함. db_query() -> db_binding_prefilter() 에서 stripcslashes() 실행됨.
 *
 * @return int
 */
function db_create($table, $attributes = array())
{
	global $db, $G_SYS;
	$columns = array();
	$values  = array();
	$oracle_sqc = null;
	foreach ($attributes as $key => $val) {
		// [2025-12-16] DB가 오라클이고 sqc 가져올 때 예외처리하기 위해 추가
		if ($G_SYS[DB] == 'oracle' && preg_match('/^sqc_[0-9A-Za-z_]+\.NEXTVAL$/', $val)) {
			$oracle_sqc = $val . ', ';
		} else {
			$values[]  = $val;
		}
		$columns[] = $key;
	}

	$query = "insert into " . $table . " (" . implode(', ', $columns) . ")"
			. " values (" . $oracle_sqc . implode(', ', array_fill(0, count($values), '?')) . ")";
	db_query($query, $values);

	return $db->affectedRows();
}

/**
 * DB 데이타 갱신
 *   - 대량 할당 지원: 키->밸류 배열로 값을 전달하면 insert 쿼리문이 자동 생성되어 실행됨
 *
 * @param string $table
 * @param array  $attributes      주의) 데이터는 $_POST, $_GET 처럼 addslashes() 적용된 상태여야 함. db_query() -> db_binding_prefilter() 에서 stripcslashes() 실행됨.
 * @param string $where           실수 예방위해 조건절 필수
 * @param array  $whereBindings   조건절 바인딩값  ex) array('1', 'test')
 *
 * @return int
 */
function db_update($table, $attributes = array(), $where, $whereBindings = array())
{
	global $db;

	if (empty($where)) {
		error('db_update 에서 $where 조건절은 필수입니다.', 'DB');
	}

	$columns = array();
	$values  = array();
	foreach ($attributes as $key => $val) {
		$values[] = $val;
		$columns[] = $key . " = ?";
	}

	$query = "update " . $table . " set " . implode(',', $columns) . " where " . $where;

	if (count($whereBindings) > 0) {
		foreach ($whereBindings as $val) {
			$values[] = $val;
		}
	}

	db_query($query, $values);

	return $db->affectedRows();
}

/**
 * 메일 수신 상태
 * - 수신 가능 RECEIVABLE
 * - 수신 불가 (탈퇴회원) UNAVAILABLE_WITHDRAWN
 * - 수신 불가 (메일용량 초과) UNAVAILABLE_OVER_QUOTA
 * - 수신 불가 (없는 아이디) UNAVAILABLE_NOT_FOUND
 * - 외부 주소 (확인불가) EXTERNAL_UNKNOWN
 *
 * @param string $email - 메일링은 m_no랑 d_no 없으므로 email 받도록 수정
 * @param int|null $m_no
 * @param int|null $d_no
 *
 * @return array
 */
function get_email_status($email, $m_no = null, $d_no = null)
{
	global $G, $G_SYS, $TB;

	$m_usertype = null;
	$m_name = null;

	// [보안] 관리자메뉴에서는 자신의 도메인만 회원 이름 및 수신 상태 조회 가능하도록 제한
	$q_where = null;
	if ($_GET['isadmin'])
	{
		$d_no = $G['DINFO']['d_no'];
		$q_where = " AND d_no='" . $d_no . "'";
	}	// if()

	$mbr = null;
	$domain = null;
	if ($m_no !== null && $d_no !== null) {
		$sql = "SELECT m_no, d_no, m_id, m_name, m_level, m_usertype, m_quota, m_mail_file_group FROM " . $TB['MEMBER']. " WHERE m_no = '" . $m_no . "'" . $q_where;
		$mbr = db_get_row($sql);

		$sql = "SELECT d_no, d_name FROM ". $TB['DOMAIN']. " WHERE d_no = '". $mbr['d_no'] ."'" . $q_where;
		$domain = db_get_row($sql);
	} else if ($email !== null) {
		list($m_id, $d_name) = explode("@", $email);

		$sql = "SELECT d_no, d_name FROM ". $TB['DOMAIN']. " WHERE d_name = '". $d_name ."'" . $q_where;
		$domain = db_get_row($sql);

		$sql = "SELECT m_no, m_id, m_name, m_level, m_usertype, m_quota, m_mail_file_group FROM " . $TB['MEMBER']. " WHERE m_id = '" . $m_id . "' AND d_no='" . $domain['d_no'] . "'";
		$mbr = db_get_row($sql);
	}

	// 조회한 회원이 있으면 m_usertype 가져오기
	if ($mbr) {
		$m_usertype = $mbr['m_usertype'];
		$m_name = $mbr['m_name'];
	}

	// d_name 이 없으면 외부 주소
	if (! $domain) {
		return array($m_usertype, 'EXTERNAL_UNKNOWN', $m_name);
	}

	// $mbr이 없으면 없는 아이디
	if (! $mbr) {
		return array($m_usertype, 'UNAVAILABLE_NOT_FOUND', $m_name);
	}

	// 탈퇴회원
	if ((int)$G['LEVEL']['out'] === (int)$mbr['m_level']) {
		return array($m_usertype, 'UNAVAILABLE_WITHDRAWN', $m_name);
	}

	// 가입대기
	if ((int)$G['LEVEL']['wait'] === (int)$mbr['m_level']) {
		return array($m_usertype, 'UNAVAILABLE_PENDING', $m_name);
	}

	// 메일 용량 가져오기
	$mail_file_table = $G_SYS[PRE_FIX] . 'mail_file_' . $domain['d_no'] . '_' . $mbr['m_mail_file_group'];
	$sql = "SELECT SUM(mf_filesize) FROM " . $mail_file_table . " WHERE m_no='" . $mbr['m_no'] . "'";
	$_mf_filesize_sum = db_get_one($sql);

	// 메일 용량 초과
	if ((int)$_mf_filesize_sum > $mbr['m_quota']*1024) {
		return array($m_usertype, 'UNAVAILABLE_OVER_QUOTA', $m_name);
	}

	return array($m_usertype, 'RECEIVABLE', $m_name);

}

/**
 * 보낸사람이 지정한 중요도 아이콘
 *   - 제목 앞에 보여줄때만 사용
 *
 * @param string $importance
 * @param bool   $list_mode 목록에서만 true 이고, 기존 중요도 별도 컬럼 표시시 제목앞에 중복되지 않도록 함
 *
 * @return string|null
 */
function mail_importance_icon($importance, $list_mode = false)
{
	global $G_SYS;

	$icon = null;
	if ($G_SYS['MAIL_IMPORTANT_NEW']) {
		// 메일 목록에서 제목 앞에 표시, 중요도 높음만 표시.
		if ($importance == 'H') {
			$icon = '<img src="' . nskindirroot('webmail')
					. '/img/mail_importance_high.svg" width="6" height="12" border="0" alt="중요도 높음" class="mail-importance high">';
		} else if ($importance == 'L') {
			// 중요도 낮음 표시안함
//			$icon = '<img src="' . nskindirroot('webmail')
//					. '/img/mail_importance_low.svg" width="6" height="12" border="0" alt="중요도 낮음" class="mail-importance low">';
		}    // if()
	} else {
		if ( ! $list_mode) {
			// 메일 목록에서 체크박스 뒤에 별도 컬럼으로 표시, 중요도 높음/낮음 표시.  (v3.3 화면 유지용)
			if ($importance == 'H') {
				$icon = '<img src="' . nskindirroot('webmail')
						. '/img/mail_importance_high.gif" width="10" height="14" border="0" alt="중요도 높음" class="mail-importance high">';
			} else if ($importance == 'L') {
				$icon = '<img src="' . nskindirroot('webmail')
						. '/img/mail_importance_low.gif" width="10" height="14" border="0" alt="중요도 낮음" class="mail-importance low">';
			}    // if()
		}
	}

	return $icon;
}

/**
 * 메일 미리보기 - 메일 본문이 클 경우를 위한 부하 감소
 *
 * @param string $mf_body 메일 본문
 * @param string|int|null $max_body 메일 본문이 길 경우 속도 향상을 위해 앞부분만 사용하여 필터링함
 * @param string|int|null $max_text 메일 본문에서 추출한 텍스트의 최대 길이
 *
 * @return string
 */
function mail_body_text_preview($mf_body, $max_body = null, $max_text = null)
{
	if ($max_body === '' || $max_body === null) {
		$max_body = 524288;
	}

	if ($max_text === '' || $max_text === null) {
		$max_text = 10240;
	}

	// 정규 표현식 부하가 적도록, 최대 본문 크기로 자르기
	$mf_body = substr($mf_body, 0, $max_body);

	// title, script, style 태그 삭제
	// br, hr 은 줄바꿈으로 치환
	$mf_body = preg_replace(
			array(
					"/<title(.*?)title>/si",
					"/<script(.*?)\/script[^>]*>/si",
					"/<style(.*?)\/style[^>]*>/si",
					"/<(br|hr)+[^>]*>/i",
					"/<\/(div|p|table|tr|li|dd|pre|blockquote)+></i",        // 태그가 1줄로 합쳐진 경우 대비하여, 블록 레벨 태그 뒤에 줄바꿈 추가
			),
			array(
					"",
					"",
					"",
					"\n",
					"</\\1>\n<",
			),
			$mf_body
	);
//	echo "<xmp>" . $mf_body . "</xmp>";

	// a 태그는 '내용 + 주소' 형식으로 보여주어 도메인 확인이 쉽게 함
	//   내용과 주소가 같은 경우 주소만 표시
	//   <a href="mailto: 등 제외하고, http/https 인 경우만 표시
	$mf_body = preg_replace_callback('/<a\s+[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/is', function ($matches) use ($mf_body) {
		$href = trim($matches[1]);
		$text = trim($matches[2]);

		if ($href === $text) {
			return han_substr($href, 50);
		}

		if (stripos($href, 'http://') === 0 || stripos($href, 'https://') === 0 ) {
			return $text . ' ' . han_substr($href, 50);
		}

		return $text;
	}, $mf_body);

	$mf_body = strip_tags($mf_body);

	// 줄바꿈 \n 로 통일 및 특수문자 치환
	$mf_body = str_ireplace(
			array(
					"\r\n",
					"\r",
					"\t",
					"&nbsp;",
					"&#32;",
					' ',
					'‌',
					'﻿',
					'​',
			),
			array(
					"\n",
					"\n",
					" ",
					" ",
					" ",
					" ",
					" ",
					" ",
					" ",
			),
			$mf_body
	);

	// 2줄 이상 줄바꿈은 1줄로 줄이기
	$patterns2 = array(
			"/\n[ \n]+/",
	);
	$replacements2 = array(
			"\n",
	);
	$mf_body = preg_replace($patterns2, $replacements2, $mf_body);

	// 텍스트 최대 길이만큼 자르고, 마지막 자리에서 한글 등 깨짐 고려하여 공백 추가
	return htmlspecialchars_fix(han_substr(trim($mf_body), $max_text)).'  ';
}

/**
 * 날짜 요일, 오전 한글로 변환
 *
 * @param string $date
 *
 * @return string|null
 */
function date_lang_kor($date)
{
	if ( ! empty($date)) {
		return str_replace(
				array(
						'Mon',
						'Tue',
						'Wed',
						'Thu',
						'Fri',
						'Sat',
						'Sun',
						'AM',
						'PM',
				),
				array(
						'월',
						'화',
						'수',
						'목',
						'금',
						'토',
						'일',
						'오전',
						'오후',
				),
				$date
		);
	}
}

/**
 * 예약 발송 포함여부
 *
 * @return bool
 */
function has_mail_send_reserve()
{
	global $G_SYS;

//	$path = dirname($G_SYS[PAGE_ROOT]).'/tools/agent/NmailAgentChild_mailreserve.inc.php';      // tools 아래는 root 권한으로 확인 불가
	$path = dirname($G_SYS[PAGE_ROOT]).'/nmail/admin_mail_send_reserve.php';

	return file_exists($path);
}

/**
 * 메일 목록 상태 아이콘
 *
 * @param string $mf_is_read
 * @param string $mf_is_reply
 * @param string $mf_is_forward
 *
 * @return string
 */
function mail_list_status_icon($mf_is_read, $mf_is_reply, $mf_is_forward)
{
	$icon = null;

	$envelope_read_status = ($mf_is_read == 'Y') ? 'bi-envelope-open' : 'bi-envelope-fill';

	if ($mf_is_reply == 'Y') {
		$icon = '<span class="reply-mail" aria-hidden="true" title="답장한 메일">
					<i class="bi bi-reply-fill"></i>
					<i class="bi ' . $envelope_read_status . '"></i>
		           </span>';
	} else if ($mf_is_forward == 'Y') {
		$icon = '<span class="forward-mail" aria-hidden="true" title="전달한 메일">
				<i class="bi bi-reply-fill"></i>
				<i class="bi ' . $envelope_read_status . '"></i>
			       </span>';
	} else if ($mf_is_read == 'Y') {
		$icon = '<span class="read-mail" aria-hidden="true" title="읽은 메일"><i class="bi bi-envelope-open"></i></span>';
	} else {
		$icon
				= '<span class="unread-mail" aria-hidden="true" title="읽지않은 메일"><i class="bi bi-envelope-fill"></i></span>';
	}    // if()

	return $icon;
}

/**
 * 용량 설정 링크
 *
 * @param string $name
 *
 * @return string
 */
function quota_plus_links($name) {
	global $G_SYS;

	$links = '';
	foreach ($G_SYS['QUOTA'] as $key => $value) {
		$links .= "<a class=\"quota-plus\" href=\"javascript:quota_plus('" . $key . "', '" . $name . "')\">" . $value . "</a>";
	}

	return $links;
}

/**
 * 비밀번호 일치 확인
 *
 * @param string $password 입력 비밀번호
 * @param string $hash     password_hash() 를 통해 해시된 문자열
 * @param string|null   $m_id
 * @param string|int|null   $m_no
 *
 * @return bool
 */
function nmail_password_verify($password, $hash, $m_id = null, $m_no = null)
{
	global $G_SYS, $TB;

	if ($G_SYS['PWD_ENC'] == 'BCRYPT') {
		if (password_verify($password, $hash)) {
			return true;
		}

		// 이전 비밀번호 해시가 없을 경우 MD5 기본 사용
		if (! isset($G_SYS['PWD_ENC_LEGACY'])) {
			$G_SYS['PWD_ENC_LEGACY'] = 'MD5';
		}

		// 이전 비밀번호 해시 방식으로 저장된 비밀번호가 있을 경우
		$verified = false;
		if ($G_SYS['PWD_ENC'] !== $G_SYS['PWD_ENC_LEGACY']) {
			// 이전 비밀번호 해시 방식으로 한번 더 검사
			if ($G_SYS['PWD_ENC_LEGACY'] === 'MD5' && strlen($hash) === 32) {
				$verified = ($hash === md5($password));
			}

			// 이전 비밀번호와 일치하고, 로그인 등에서 회원번호가 입력된 경우
			if ($verified && $m_no !== null) {
				// 신규 비밀번호 해시로 마이그레이션
				db_update($TB['MEMBER'], array('m_pwd' => pwd_enc($password, $m_id)), "m_no = ?", array($m_no));

				errorlog(
						"[nmail_password_verify] " . $m_id . " 비밀번호 해시 마이그레이션 완료. "
						. $G_SYS['PWD_ENC_LEGACY'] . " -> " . $G_SYS['PWD_ENC'], "MEMBER"
				);
			}
		}

		return $verified;
	} else {
		// BCRYPT 가 아닐때 이전 해시로 검사
		return ($hash === pwd_enc($password, $m_id, $hash));
	}
}

/**
 * 로그인 회원의 2단계 인증 정보
 *
 * @return array|false
 */
function member_2fa_info()
{
	global $TB, $AUTH;

	$q = "SELECT m_2fa_isuse, m_2fa_required, m_2fa_code FROM $TB[MEMBER] WHERE m_no=?";
	$mbr = db_get_row($q, array($AUTH['auth_m_no']));

	if ($mbr == NULL) {
		msg("존재하지 않는 회원입니다.");
	}	// if()

	return $mbr;
}

/**
 * 날짜 표시
 *   - 수정일 등이 저장되지 않아 0000-00-00 등으로 보이지 않도록 보완
 *
 * @param string $date
 *
 * @return string
 */
function date_view($date, $format = 'Y-m-d H:i')
{
	if ($date == '0000-00-00 00:00:00' || $date == '0000-00-00' || $date == null) {
		return '';
	}

	// 날짜만 입력된 경우 기본 포맷을 날짜로 변경
	if (strlen($date) === 10 && $format === 'Y-m-d H:i') {
		$format = 'Y-m-d';
	}

	try {
		$dt = new DateTime($date);

		return $dt->format($format);
	} catch (Exception $e) {
		//
	}

	return $date;
}

/**
 * 날짜 기간 계산
 *
 * @param string $date
 * @param string $format
 * @param string $type +, -
 * @param string $duration P1D = period 1 days  https://www.php.net/manual/en/dateinterval.construct.php
 *
 * @return string
 * @throws DateInvalidOperationException
 */
function date_interval($date = null, $format = 'Y-m-d H:i', $type = '+', $duration = 'P0D')
{
	$date = new DateTime($date);
	$interval = new DateInterval($duration);

	if ($type === '+') {
		$date->add($interval);
	} else {
		$date->sub($interval);
	}

	return $date->format($format);
}

/**
 * 시작일
 *
 * @param string $date
 *
 * @return false|string
 */
function date_start($date)
{
	if (($time = strtotime($date)) === false) {
		return false;
	}

	return date('Y-m-d 00:00:00', $time);
}

/**
 * 종료일
 *
 * @param string $date
 *
 * @return false|string
 */
function date_end($date)
{
	if (($time = strtotime($date)) === false) {
		return false;
	}

	return date('Y-m-d 23:59:59', $time);
}

/**
 * 세션 로그아웃
 *
 * @param $m_no
 * @param $device_token
 *
 * @return void
 */
function session_logout($m_no, $device_token = null)
{
	global $UPLOAD_ROOT, $TB;

	$path = $UPLOAD_ROOT . '/session';
	$m_path = $UPLOAD_ROOT . '/mobile_session';

	$q = "SELECT * FROM " . $TB['MEMBER_DEVICE'] . " WHERE m_no='" . $m_no . "'";

	if ($device_token !== null)
		$q .= " AND device_token='" . $device_token ."'";

	$devices = db_get_rows($q);

	foreach ($devices as $device) {
		$is_file = false;
		if (file_exists($path . '/sess_' . $device['session_id'])) {
			$is_file = true;
			unlink($path . '/sess_' . $device['session_id']);
		}

		if (file_exists($m_path . '/sess_' . $device['session_id'])) {
			$is_file = true;
			unlink($m_path . '/sess_' . $device['session_id']);
		}

		if (! $is_file){
			errorlog("로그아웃이 실패하였습니다. 삭제된 세션 ID입니다.\n=> session_id : " . $device['session_id'], 'DEBUG');
		}

		// db del
		$sql = " DELETE FROM " . $TB['MEMBER_DEVICE'] . " WHERE m_no='" . $device['m_no'] . "' AND device_token='"
				. $device['device_token'] . "'";
		db_query($sql);
	}
}

/**
 * 왼쪽 메뉴 너비
 *   - 사용자가 변경한 경우 쿠키값 반영
 *
 * @param bool $is_sidebar false: 우측 컨텐츠
 *
 * @return string
 */
function get_nm_sidebar_width_style($is_sidebar = true)
{
	global $G_SYS;

	if (
			isset($_COOKIE['nm_sidebar_width'])
			&& is_numeric($_COOKIE['nm_sidebar_width'])
			&& $_COOKIE['nm_sidebar_width'] >= $G_SYS['NM_SIDEBAR_WIDTH_MIN']
			&& $_COOKIE['nm_sidebar_width'] <= $G_SYS['NM_SIDEBAR_WIDTH_MAX']
	) {
		if ($is_sidebar) {
			return ' style="width:' . $_COOKIE['nm_sidebar_width'] . 'px"';
		} else {
			return ' style="width:calc(100% - ' . ($_COOKIE['nm_sidebar_width'] + $G_SYS['NM_SIDEBAR_DIVIDER_WIDTH']) . 'px)"';
		}
	}
}

/**
 * 메뉴 CSS 클래스
 *
 * @param string $menu_id
 * @param string $bm_no
 *
 * @return string|void
 */
function get_nm_menu_active($menu_id, $bm_no = null)
{
	global $G, $TB, $AUTH;

	if (in_array($G['THISPAGETYPE'], array('ADMIN', 'SADMIN'))) {
		return '';
	}

	$current_menu_id = null;

	if (! isset($GLOBALS['__current_menu_id']) || $bm_no !== null ) {
		if ($G['THISPAGE'] === 'mail_list.php' && ! $_GET['mb_id']) {
			$current_menu_id = 'left_mail_list';
		} else if (strpos($_SERVER['PHP_SELF'], 'board.php') !== false
				|| strpos($_SERVER['PHP_SELF'], 'board_new.php') !== false) {

			if ($_GET['bm_no'] && $_GET['bm_no'] === $bm_no) {
				$current_menu_id = 'board';
			} else if ($_GET['ba_no'] && $bm_no !== null) {
				$_bm_no = db_get_one("SELECT bm_no FROM " . $TB['BOARD_ADMIN'] . " WHERE ba_no='" . $_GET['ba_no'] . "' AND d_no='" . $AUTH['auth_d_no'] . "'");

				if ($_bm_no === $bm_no) {
					$current_menu_id = 'board';
				}
			} else if ($menu_id === 'mail') {
				return '';
			}
		} else if (in_array($G['THISPAGE'], array('diary_month.php', 'diary_add.php', 'diary_list.php'))) {
			$current_menu_id = 'diary';
		} else if (in_array($G['THISPAGE'], array('address.php'))) {
			$current_menu_id = 'address';
		} else if (strpos($_SERVER['PHP_SELF'], '/webhard/') !== false) {
			$current_menu_id = 'webhard';
		} else if (in_array($G['THISPAGE'], array(
						'mail_config.php', 'default_config.php', 'mail_box.php', 'mail_box_add.php', 'mail_box_mod.php',
						'sign.php',
						'auto_reply.php', 'mail_forward.php', 'pop3.php', 'pop3_mod.php', 'spam_config.php',
						'auto_filter_list.php', 'auto_filter_add.php', 'auto_filter_mod.php', 'spam_allow.php',
						'spam_reject.php', '2fa_config.php'
				))
				|| ($G['THISPAGE'] === 'member.php' && $_GET['mode'] === 'OUT')) {
			$current_menu_id = 'mail_config';
		} else if ($G['THISPAGE'] === 'member.php' && $_GET['mode'] === 'MODIFY') {
			$current_menu_id = 'modify';
		} else if ($G['THISPAGE'] === 'index.php') {
			$current_menu_id = 'main';
		}

		$GLOBALS['__current_menu_id'] = $current_menu_id;
	}

	if ($menu_id === $GLOBALS['__current_menu_id']) {        // 현재 주소와 메뉴가 일치시 활성화
		return ' active';
	} else if ($menu_id === 'mail' && ($GLOBALS['__current_menu_id'] === null || $GLOBALS['__current_menu_id'] === 'left_mail_list')) {       // 현재 메뉴 미일치하거나, 왼쪽 메뉴 전체메일일때 '메일'
		return ' active';
	}
}

/**
 * 스팸포인트 css 클래스
 *
 * @param $spam_point
 *
 * @return false|float|int
 */
function get_spam_point($spam_point)
{
	if ($spam_point < 0) {
		return 0;
	} elseif ($spam_point > 9) {
		return 9;
	} else {
		return ceil($spam_point);
	}
}

/**
 * 엔메일 인코딩 소스 파일
 *
 * @param string $filename
 * @param string $type standard, premium
 *
 * @return string
 */
function nmail_encoded_lib_file($filename, $type = 'standard')
{
	// [2025-12-17] 프리미엄 안티스팸 - 사용할 업데이트된 파일명(AUTO, 빌드일시 선택)
	if ($filename === 'mail_premium_spam_lib.php') {
		$nmailSpamUpdate = new NmailSpamUpdate;
		if ($updatedFile = $nmailSpamUpdate->getUpdatedFile()) {
			return $updatedFile;
		}
	}

	$__include_src = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . '__include_src';
	if (file_exists($__include_src . DIRECTORY_SEPARATOR . $filename)) {
		return $__include_src . DIRECTORY_SEPARATOR . $filename;
	} else {
		$php_version = (version_compare(phpversion(), '5.6.0', '>='))
				? 'php56'
				: 'php53';

		$include_enc = dirname(__DIR__)
				. DIRECTORY_SEPARATOR . 'include_enc' . DIRECTORY_SEPARATOR . $type . '_' . $php_version;
		if (file_exists($include_enc . DIRECTORY_SEPARATOR . $filename)) {
			return $include_enc . DIRECTORY_SEPARATOR . $filename;
		} else {
			if ($type === 'standard') {
				error(
						"엔메일 소스 파일이 존재하지 않습니다. => "
						. $include_enc . DIRECTORY_SEPARATOR . $filename, 'SYSTEM'
				);
			}
		}
	}
}

/**
 * 메일 주소에서 도메인만 가져오기
 *    "홍길동" <test@domain.com> -> domain.com
 *    <test@domain.com>          -> domain.com
 *    test@domain.com            -> domain.com
 *
 * @param string $address
 *
 * @return string|null
 */
function get_mail_domain($address)
{
	$domain = null;
	if ($address !== null && trim($address) !== '') {
		$parse_address = mime_address_parse($address);
		if (isset($parse_address['address'])) {
			list(, $domain) = explode('@', strtolower($parse_address['address']));
		}
	}

	return $domain;
}

/**
 * 엔메일 CLI 타이틀 출력
 *
 * @param string $message
 * @param bool   $no_view
 *
 * @return void
 */
function nmail_cli_title($message, $no_view = true, $suffix = '# ')
{
	if ($no_view) {
		$GLOBALS['__NMAIL_CLI_TITLE_NO'] = isset($GLOBALS['__NMAIL_CLI_TITLE_NO'])
				? ++$GLOBALS['__NMAIL_CLI_TITLE_NO']
				: 1;

		$message = $suffix . "[" . $GLOBALS['__NMAIL_CLI_TITLE_NO'] . "] " . $message;
	} else {
		$message = $suffix . $message;
	}

	// [2026-05-15] CLI 타이틀 출력 기록
	$GLOBALS['__NMAIL_CLI_TITLE_HISTORY'] = isset($GLOBALS['__NMAIL_CLI_TITLE_HISTORY'])
		? $GLOBALS['__NMAIL_CLI_TITLE_HISTORY'] . PHP_EOL . $message . PHP_EOL
		: PHP_EOL . $message . PHP_EOL;

	if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
		$message = "\033[0;32m" . $message . "\033[0m";
	}

	echo PHP_EOL . $message . PHP_EOL;
}

/**
 * 엔메일 CLI 에러 출력후 중단
 *
 * @param string $message
 * @param bool   $exit
 *
 * @return void
 */
function nmail_cli_error($message, $exit = true)
{
	if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
		$message = "\033[0;41m" . $message . "\033[0m";
	}

	echo PHP_EOL . $message . PHP_EOL;

	if ($exit) {
		exit(1);
	}
}
