//networkIsolation 网络隔离
import sys.acl;
import process;

namespace sys.networkIsolation;
	
try{
::FirewallApi = ..raw.loadDll("Firewallapi.dll");
}

class INET_FIREWALL_APP_CONTAINER {
	ptr appContainerSid;
	ptr userSid;
	ustring appContainerName;
	ustring displayName;
	ustring description;
	struct capabilities = {
		INT count;
		ptr capabilities;
	};
	struct binaries = {
		INT count;
		ptr binaries;
	};
	ustring workingDirectory;
	ustring packageFullName;
}  

var sidToString = ..sys.acl.sidToString;

getAppContainers = function(){
    var enumAppContainers = ::FirewallApi.NetworkIsolationEnumAppContainers;
    if(!enumAppContainers){
    	return {};
    }
    
	var numPublicAppCs = {INT count}
	var publicAppCs = {ptr ptr}
	if( 0 == enumAppContainers(2/*NETISO_FLAG_MAX*/,numPublicAppCs,publicAppCs) ){
		var apps = ..raw.convertArray(publicAppCs.ptr,numPublicAppCs.count,INET_FIREWALL_APP_CONTAINER)
		for(i=1;#apps;1){
			apps[i].sid = sidToString(apps[i].appContainerSid)
		} 
		
		::FirewallApi.NetworkIsolationFreeAppContainers(publicAppCs.ptr);
		
		var exemptApps = getLoopbackState();
		for(i,cfg in configs){ exemptApps[cfg.sid] = true; } 
		 
		for(k,app in apps){
			app.loopback = exemptApps[app.sid];
		}
	
		return apps;
	}
}
 
getAppContainerByName = function(name) {
	var i,v = ..table.find(getAppContainers(),lambda(ct) ..string.cmp(ct.appContainerName,name)==0 );
	return v;
} 

getAppContainerConfig = function(){
	var numPublicAppCs = {INT count}
	var publicAppSids = {ptr ptr}
	if( 0 == ::FirewallApi.NetworkIsolationGetAppContainerConfig(numPublicAppCs,publicAppSids) ){
		var configs = ..raw.convertArray(publicAppSids.ptr,numPublicAppCs.count,{ ptr sid;INT attributes;})
		if(!configs) return {};
		
		var heap = ::Kernel32.GetProcessHeapP();
	 	for(i=1;#configs;1){
	 		var sid = configs[i].sid;
			configs[i].sid = sidToString(sid)
			::Kernel32.HeapFree(heap, 0, sid);
		}  
    
    	::Kernel32.HeapFree(heap, 0, publicAppSids.ptr);
		return configs;
	}
	
	return {};
} 

getLoopbackState = function(){
	var configs = getAppContainerConfig();
	
	var result = {};
	for(i,cfg in configs){
		result[cfg.sid] = true;
	} 
	
	return result;
}

var sidFromString = ..sys.acl.sidFromString;
enableLoopback = function(sids){
	var configs = getAppContainerConfig();
	for(i=#configs;1;-1){
		if(sids[configs[i].sid]!==null){
			..table.remove(configs,i);
		} 
	}
	
	for(sid,enabled in sids){
		if(enabled){
			..table.push(configs,{
				ptr sid = sid;
				INT attributes;
			});
		}
	}
	
	for(i=#configs;1;-1){
		configs[i].sid = sidFromString(configs[i].sid);
	}
	
	var e = ::FirewallApi.NetworkIsolationSetAppContainerConfig(#configs,#configs?{ struct configs[] = configs }:null);
	if(e){ return false,..lasterr(e); }
	return true;
}

exempt = function(name,disabled){
    var cts = getAppContainers();
    for(k,ct in cts){
    	if((..string.cmp(ct.appContainerName,name)==0) || (ct.sid==name)){
    		if(ct.loopback==!disabled){
    			return true;
    		}
    		
    		return enableLoopback({[ct.sid]=!disabled})
    	} 
    } 	
}

exemptAs = function(name,disabled){
    var cts = getAppContainers();
    for(k,ct in cts){
    	if((..string.cmp(ct.appContainerName,name)==0) || (ct.sid==name)){
    		if(ct.loopback==!disabled){
    			return true;
    		}
    		
    		return !!..process.executeWait("CheckNetIsolation.exe", `LoopbackExempt ` +(disabled?"-d":"-a")+` -p="`+ct.sid+`"`,"runas",0/*_SW_HIDE*/)
    	} 
    } 	
}

/**intellisense(sys.networkIsolation)
getAppContainers() = 返回包含所有APP容器信息的数组
getAppContainerByName(.(name) = 使用容器名查找并返回容器信息 
getAppContainerConfig() = 获取所有APP容器的本机网络配置,\n返回值是一个数组，数组无素请查看此函数源码
getLoopbackState() = 获取所有APP容器的本机网络配置,\n返回值是一个表，键为SID字符串，值指定是否允许访问本机网络
enableLoopback(sidConfigs) = 允许或禁用指定APP容器是否可以访问本机网络\n参数必须是一个表，键为SID字符串，值指定是否允许访问本机网络,\n仅修改指定SID的配置,\n成功返回true,失败返回false,错误信息
exempt(.(nameOrSid,disabled) = 允许或禁用指定APP容器是否可以访问本机网络\n@nameOrSid参数指定应用容器名或SID,\n可选参数@disabled指定是否禁用访问本机网络,\n成功返回true,失败返回false,错误信息,\n如果无需修改直接返回true,\n否则进程不是以管理权限启动时会失败并返回false
exemptAs(.(nameOrSid,disabled) = 以管理权限允许或禁用指定APP容器是否可以访问本机网络\n@nameOrSid参数指定应用容器名或SID,\n可选参数@disabled指定是否禁用访问本机网络,\n成功返回true,失败返回false,错误信息,\n如果无需修改直接返回true,
end intellisense**/

/**intellisense()
sys.networkIsolation = 用于管理 UWP 应用网络隔离配置,仅适用于 WIN10。
end intellisense**/