﻿/* VirusTotal Command for Directory Opus
**Perform a virus scan check for a file using the VirusTotal API**
VirusTotalCommand © 2024-2025 by Christian Arellano García
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
*/
var script_name = 'VT';
var script_version = '1.5.2';
var FSU, str_tools;
// Called by Directory Opus to initialize the script
function OnInit(initData) {
	initData.name = script_name;
	initData.version = script_version;
	initData.copyright = '(c) 2025 Cristian Arellano García';
	initData.desc = 'Perform a virus scan check for a file using the VirusTotal API';
	initData.default_enable = true;
	initData.min_version = '13.14';
	initData.config = DOpus.Create().OrderedMap();
	initData.config_desc = DOpus.Create().OrderedMap();
	initData.config_groups = DOpus.Create().OrderedMap();
	initData.config_group_order = DOpus.NewVector('General', 'Advanced');
	AddConfig('log level', DOpus.NewVector(2, 'debug', 'standard', 'warning', 'off'), DOpus.strings.Get('cfg_log_level'), 'General');
	AddConfig('max timeout', 30, DOpus.strings.Get('cfg_max_timeout'), 'General');
	AddConfig('force file report after analysis', false, DOpus.strings.Get('cfg_force_fullreport'), 'Advanced');
	AddConfig('allow multiple instances', false, DOpus.strings.Get('cfg_allow_multi'), 'Advanced');
	AddConfig('max threads', DOpus.NewVector(1, 1, 2, 3, 4), DOpus.strings.Get('cfg_max_threads'), 'Advanced');

	function AddConfig(name, value, desc, group) {
		initData.config[name] = value;
		initData.config_desc(name) = desc;
		initData.config_groups(name) = group;
	}
}

// Called to add commands to Opus
function OnAddCommands(addCmdData) {
	var cmd = addCmdData.AddCommand();
	cmd.name = script_name;
	cmd.method = 'OnFileScan';
	cmd.desc = 'Perform a virus scan check for a file using the VirusTotal API';
	cmd.template = 'FILE/M,SETKEY/S,DELKEY/O[quiet],URLTOCLIP/S,NOGUI/O[clip,jsonclip,json,notify],AUTOUPLOAD/S';
	cmd.hide = false;
	cmd.noprogress = true;
	cmd.icon = 'bug';
	var cmd2 = addCmdData.AddCommand();
	cmd2.name = 'GetHash';
	cmd2.method = 'GetHash';
	cmd2.desc = '';
	cmd2.template = 'FILE,HASH/K[md5,sha256,blake3,crc32]';
	cmd2.hide = true;
	cmd2.icon = 'script';
	cmd2.noprogress = true;
}

function GetHash(scriptCmdData) {
	if (!scriptCmdData.func.args.got_arg.file) {
		Log(4, 'No file was given to get its hash!');
		return;
	}
	FSU = DOpus.FSUtil();
	var file = scriptCmdData.func.args.file;
	var hash;
	Log(2, 'Hashing ' + file + '...');
	try {
		hash = FSU.hash(file, scriptCmdData.func.args.got_arg.hash ? scriptCmdData.func.args.hash : 'sha256');
	}
	catch (err) {
		Log(3, 'Unable to hash ' + file + ':' + err.description);
		hash = 0;
	}
	Log(2, 'Hashing finished for ' + file + '...');
	DOpus.SendCustomMsg(file, DOpus.NewMap(file, hash));
	// Log(1, ' ================================== ');
	return;
}

function OnFileScan(scriptCmdData) {
	DOpus.ClearOutput();
	Log(2, ' =========== ' + script_name + ' v' + script_version + ' =========== ');
	Log(1, 'cmdline     : ' + scriptCmdData.cmdline);
	str_tools = DOpus.Create().StringTools();
	FSU = DOpus.FSUtil();
	if (CheckNewVT()) {
		MsgDlg('You already have the new v2. You can\'t have the two installed since it will interfere each other, so this one will be uninstalled', 0);
		UninstallScript();
		return;
	}
	if (scriptCmdData.func.args.got_arg.setkey) {
		SetAPIKey(scriptCmdData.func.Dlg());
		return;
	}
	if (scriptCmdData.func.args.got_arg.delkey) {
		DelAPIKey(scriptCmdData.func.args.delkey);
		return;
	}

	var attempts = 0;
	var api_key;
	while (!api_key) {
		api_key = GetAPIKey();
		if (!api_key) {
			if (attempts === 0) SetAPIKey(scriptCmdData.func.Dlg());
			else {
				MsgDlg(DOpus.strings.Get('msg_no_api_key'), 0, '', scriptCmdData.func.sourcetab);
				Log(4, 'API key is mandatory for this command to run!');
				return;
			}
			attempts++;
		}
	}
	FSU = DOpus.FSUtil();
	var FORCE_NOTIFY;
	var NOGUI = scriptCmdData.func.args.got_arg.NOGUI ? scriptCmdData.func.args.NOGUI : '';
	if (NOGUI) {
		var args_nogui = NOGUI.split(',');
		for (var i = 0; i < args_nogui.length; i++) {
			switch (args_nogui[i].trim()) {
				case 'notify':
					FORCE_NOTIFY = true;
					break;
				case 'jsonclip':
					NOGUI = 1;
					break;
				case 'json':
					NOGUI = 2;
					break;
				case 'clip':
					NOGUI = 3;
					break;
				default:
					NOGUI = 4;
					break;
			}
		}
	}
	var AUTOUPLOAD = scriptCmdData.func.args.got_arg.AUTOUPLOAD;
	var force_file_request = Script.config['force file report after analysis'];
	Log(1, 'NOGUI       : ' + NOGUI);
	Log(1, 'AUTOUPLOAD  : ' + AUTOUPLOAD);
	var cmd = scriptCmdData.func.command;

	var files = GetFiles(scriptCmdData.func.args.got_arg.file ? scriptCmdData.func.args.file : cmd.files, NOGUI);
	if (!files.empty) {
		cmd.ClearFiles();
		var tab = scriptCmdData.func.sourcetab;
		var flagged_files = DOpus.NewMap();
		var dlg = {};
		dlg.main = scriptCmdData.func.Dlg();
		dlg.main.template = 'main';
		var icoObj = GetIcoObj();
		if (icoObj && icoObj.type) {
			// Log(1, icoObj.type + ':' + icoObj.width + ':' + icoObj.height);
			dlg.main.icon = icoObj;
		}
		if (NOGUI) dlg.main.msgonly = true;
		else if (!Script.config['allow multiple instances']) dlg.main.singleton = script_name + '_' + script_version;

		if (dlg.main.Create()) {
			//========DIALOG VARIABLES=============
			dlg.file_name = dlg.main.Control('filename');
			dlg.file_info_size = dlg.main.Control('file_info_size');
			dlg.file_info_hash = dlg.main.Control('file_info_hash');
			dlg.file_info_date = dlg.main.Control('file_info_date');
			dlg.stats1 = dlg.main.Control('stats1', 'tab_vendors', 'tabs');
			dlg.stats2 = dlg.main.Control('stats2', 'tab_vendors', 'tabs');
			dlg.stats3 = dlg.main.Control('stats3', 'tab_vendors', 'tabs');
			dlg.stats4 = dlg.main.Control('stats4', 'tab_vendors', 'tabs');
			dlg.banner = dlg.main.Control('banner');
			dlg.list_vendors = dlg.main.Control('list_vendors', 'tab_vendors', 'tabs');
			dlg.search_vendors = dlg.main.Control('filter_vendors', 'tab_vendors', 'tabs');
			dlg.search_btn_vendors = dlg.main.Control('search_btn', 'tab_vendors', 'tabs');
			dlg.clear_btn_vendors = dlg.main.Control('clear_btn', 'tab_vendors', 'tabs');
			dlg.list_details = dlg.main.Control('list_details', 'tab_details', 'tabs');
			dlg.list_sandbox = dlg.main.Control('list_sandbox', 'tab_details', 'tabs');
			dlg.list_sa = dlg.main.Control('list_sigma_analysis', 'tab_details', 'tabs');
			dlg.group_sandbox = dlg.main.Control('group_sandbox', 'tab_details', 'tabs');
			dlg.group_sa = dlg.main.Control('group_sigma_analysis', 'tab_details', 'tabs');
			dlg.track_bar = dlg.main.Control('progress_track');
			dlg.progress_bar = dlg.main.Control('progress_bar');
			dlg.close_btn = dlg.main.Control('close_btn');
			dlg.report_btn = dlg.main.Control('report_btn');

			var report_menu = DOpus.NewVector(DOpus.strings.Get('label_menu_json_clip'), DOpus.strings.Get('label_menu_json_file'), DOpus.strings.Get('label_menu_text_clip'), DOpus.strings.Get('label_menu_text_file'));
			var reports_set = DOpus.NewVector('type_description', 'magic', 'creation_date', 'last_modification_date', 'first_seen_itw_date', 'first_submission_date',
				'last_submission_date', 'times_submitted', 'names', 'meaningful_name', 'tags');
			var cat_map = DOpus.Create().OrderedMap('total', DOpus.strings.Get('label_stats_total'), 'timeout', DOpus.strings.Get('label_stats_timeout'), 'harmless', DOpus.strings.Get('label_stats_harmless'),
				'failure', DOpus.strings.Get('label_stats_error'), 'suspicious', DOpus.strings.Get('label_stats_susp'), 'malicious', DOpus.strings.Get('label_stats_danger'), 'type-unsupported', DOpus.strings.Get('label_stats_unsup'),
				'undetected', DOpus.strings.Get('label_stats_undet'), 'confirmed-timeout', DOpus.strings.Get('label_stats_timeout'));
			var max_timeout = Script.config['max timeout'];
			if (max_timeout < 0) max_timeout = 0;
			else if (max_timeout > 60) max_timeout = 60;
			Log(2, 'max_timeout : ' + max_timeout + 's');

			var strings = {
				'complete': str_tools.LanguageStr(7319).replace('&', ''),
				'error': str_tools.LanguageStr(6337).replace('&', ''),
				'msg_get_hash': DOpus.strings.Get('msg_get_hash'),
				'msg_waiting_label': DOpus.strings.Get('msg_waiting_label'),
				'msg_upload_success': DOpus.strings.Get('msg_upload_success'),
				'msg_check_exists_VT': DOpus.strings.Get('msg_check_exists_VT'),
				'error_reading_response': DOpus.strings.Get('error_reading_response'),
				'msg_possible': DOpus.strings.Get('msg_string_possible'),
				'msg_analysis_incomplete': DOpus.strings.Get('msg_analysis_incomplete')
			};

			var progress_width = ~~((dlg.track_bar.cx - 4) / 5); //width of the progress indicator
			var progress_step = ~~(progress_width / 3); //size of pixels to move per step
			var bar_ini_x = dlg.track_bar.x + 2;
			StartProgress();

			dlg.report_btn.label = DOpus.strings.Get('label_savereport');
			dlg.search_vendors.cuetext = str_tools.LanguageStr(9402);
			dlg.clear_btn_vendors.SetTooltip(str_tools.LanguageStr(9408).replace('&', ''));
			with(dlg.list_vendors.columns) {
				GetColumnAt(0).name = str_tools.LanguageStr(10); //Vendor
				GetColumnAt(1).name = str_tools.LanguageStr(6491); //version
				GetColumnAt(2).name = DOpus.strings.Get('header_result'); //Result
				GetColumnAt(3).name = str_tools.LanguageStr(312); //type
			}

			with(dlg.main.Control('tabs')) {
				label(0) = DOpus.strings.Get('label_vendors');
				label(1) = DOpus.strings.Get('label_report');
			}

			dlg.main.Control('file_info_titles').label = '<b><#%vs_listview_header_text>' + DOpus.strings.Get('label_size') + '\nSHA256\n' + DOpus.strings.Get('label_analyzed') + '</#></b>';
			var has_clip = '';
			var wild_obj = FSU.NewWild();
			var req_map = DOpus.NewMap();
			var total_files = files.count;
			var processed_files = DOpus.Create().StringSet();
			var response, req, msg, banner_text, max_bar_width, raw_data;
			var ui_status = 'processing';
			var curr_time = 0;
			var max_threads = NOGUI ? Script.config['max threads'] + 1 : 1;
			Log(2, 'MAX THREADS : ' + max_threads);
			var files_enum = new Enumerator(files);
			var counter = 0;
			for (; !files_enum.atEnd(); files_enum.moveNext()) {
				var curr_file = files(files_enum.item())('item');
				if (processed_files.Exists(curr_file)) continue;
				dlg.main.AddCustomMsg(curr_file.def_value, true);
				if (GetFileHash(curr_file)) SetNotifyBanner(curr_file, strings.msg_get_hash, 'add');
				else StopProcessing(curr_file, 'error', DOpus.strings.Get('error_reading_hash'));
				counter++;
				if (counter >= max_threads) {
					// Log(1, 'Stopping the hashing at ' + counter);
					break;
				}
			}
			if (!NOGUI) {
				SetDlgFileInfo(curr_file);
				dlg.main.SetTimer(200, 'progress_bar');
			}
			// var ini_time = new Date();
			dlg.main.Show();

			while (true) {
				msg = dlg.main.GetMsg();
				if (!msg.result) break;
				if (msg.event === 'custom' && files.Exists(msg.name)) {
					Log(1, '=> event : ' + msg.event + '\t; control : ' + msg.control);
					files(msg.name)('hash') = msg.object.Get(msg.name);
					if (files(msg.name)('hash') !== 0) {
						Log(2, msg.name + ' : SHA256=' + files(msg.name)('hash'));
						dlg.file_info_hash.label = files(msg.name)('hash');
						SendRequest(files(msg.name)('item'), 'get_report', strings.msg_check_exists_VT, 'https://www.virustotal.com/api/v3/files/' + files(msg.name)('hash'));
					}
					else StopProcessing(msg.name, 'error', DOpus.strings.Get('error_reading_hash'));
				}
				if (msg.event === 'http') { //http request event
					Log(1, '=> event : ' + msg.event + '\t; control : ' + msg.name + '\t; value : ' + msg.value);
					if (msg.value == 'complete') continue;
					if (!req_map.Exists(msg.name)) {
						Log(3, '=>' + msg.name + ' request is not registered!');
						continue;
					}
					req = req_map(msg.name)('req');
					if (req.complete) {
						Log(1, 'Request ' + msg.name + ' is complete');
						// StopProcessing(curr_file, 'error', DOpus.strings.Get('error_reading_response'));
						continue;
					}
					curr_file = req_map(msg.name)('file');
					if (processed_files.Exists(curr_file)) {
						Log(1, '=>' + curr_file + ' is already processed!');
						continue;
					}
					if (files(curr_file)('status_id') != req_map(msg.name)('status_id')) {
						Log(1, '=>' + curr_file + ' status_id received is "' + req_map(msg.name)('status_id') + '" , expected "' + files(curr_file)('status_id') + '"');
						continue;
					}
					switch (msg.value) {
						case 'data':
							Log(1, curr_file + ' => ' + req.responsecode + ': ' + files(curr_file)('status_id'));
							dlg.main.KillTimer('request_' + req.id);
							if (req.responsecode === 200) {
								try {
									Log(1, curr_file + ' => ' + req.id + '=> response   : ' + req.response + '\t; res code   : ' + req.responsecode + '\t; contenttype : ' + req.contenttype);
									raw_data = req.ReadResponse();
									// Log(1, 'raw:' + raw_data);
									response = JSON.parse(raw_data);
									switch (files(curr_file)('status_id')) {
										case 'get_report':
											if ('data' in response && 'attributes' in response.data) {

												StopProcessing(curr_file, 'complete', 'https://www.virustotal.com/gui/file/' + files(curr_file)('hash'), response, raw_data);
											}
											else {
												StopProcessing(curr_file, 'error', DOpus.strings.Get('error_get_report'));
											}
											break;
										case 'get_file_url':
											if ('data' in response) {
												SendRequest(curr_file, 'post_file_upload', DOpus.strings.Get('msg_uploading_file'), response.data);
											}
											else {
												StopProcessing(curr_file, 'error', DOpus.strings.Get('error_get_file_url'));
											}
											break;
										case 'post_file_upload':
											if ('data' in response && 'id' in response.data && response.data.id) {
												Log(1, curr_file + ' => post_file_upload_id=' + response.data.id);
												files(curr_file)('endpoint') = 'https://www.virustotal.com/api/v3/analyses/' + response.data.id;
												banner_text = strings.msg_upload_success;
												if (!NOGUI) {
													files(curr_file)('link') = 'https://www.virustotal.com/gui/file/' + files(curr_file)('hash');
													SetWaitTimer(curr_file, 20);
												}
												else StopProcessing(curr_file, 'complete', 'https://www.virustotal.com/gui/file/' + files(curr_file)('hash'));
												banner_text += ': <a id="link">' + files(curr_file)('link') + '</a>' + '\n';
											}
											else {
												Log(3, curr_file + ' => response.id doesn\'t exists in result');
												StopProcessing(curr_file, 'error', DOpus.strings.Get('error_file_upload'));
											}
											break;
										case 'get_analysis':
											if ('data' in response && 'attributes' in response.data) {
												Log(1, curr_file + ' => get_analysis_status=' + response.data.attributes.status);
												if (response.data.attributes.status == 'completed') {
													if (force_file_request) {
														SendRequest(curr_file, 'get_report', strings.msg_check_exists_VT, 'https://www.virustotal.com/api/v3/files/' + files(curr_file)('hash'));
													}
													else {
														StopProcessing(curr_file, 'complete', 'https://www.virustotal.com/gui/file/' + files(curr_file)('hash'), response, raw_data);
													}
												}
												else {
													banner_text = strings.msg_analysis_incomplete.replace('%s', '<a id="link">' + files(curr_file)('link') + '</a>') + '\n';
													// files(curr_file)('endpoint') = files(curr_file)('link');
													SetWaitTimer(curr_file, 20);
												}
											}
											else {
												StopProcessing(curr_file, 'error', DOpus.strings.Get('error_get_analysis'));
											}
											break;
										case 'get_remaining_quota':
											if ('data' in response) {
												var h_allowed = parseInt(response.data.api_requests_hourly.user.allowed, 10);
												var h_used = parseInt(response.data.api_requests_hourly.user.used, 10);
												Log(1, 'get_remaining_quota : allowed hourly=' + h_allowed + '; used=' + h_used);
												if (h_allowed > h_used) {
													SetWaitTimer(curr_file);
												}
												else {
													StopProcessing(curr_file, 'error', DOpus.strings.Get('error_exceeded_quota'));
												}
											}
											else {
												StopProcessing(curr_file, 'error', DOpus.strings.Get('error_get_analysis'));
											}
											break;
									}
								}
								catch (err) {
									Log(3, curr_file + ' => ' + req.id + '=> Error when reading response : ' + err.description);
									StopProcessing(curr_file, 'error', strings.error_reading_response);
								}
							}
							else if (req.responsecode === 404 && files(curr_file)('status_id') === 'get_report') {
								try {
									dlg.progress_bar.visible = false;
									banner_text = DOpus.strings.Get('msg_file_not_found_VT');
									if (AUTOUPLOAD || (!NOGUI && DOpus.Dlg.Request(banner_text + '\n' + DOpus.strings.Get('msg_upload_file_confirm'), str_tools.LanguageStr(5639) + '|' + str_tools.LanguageStr(5640), banner_text, dlg.main) == 1)) {
										banner_text += ' ' + DOpus.strings.Get('msg_uploading_file');
										dlg.progress_bar.visible = true;
										if (curr_file.size > 33554400) { //bigger than 32mb
											Log(2, curr_file + ' is bigger than 32 mb...A special url is needed');
											SendRequest(curr_file, 'get_file_url', DOpus.strings.Get('msg_get_url_file'), 'https://www.virustotal.com/api/v3/files/upload_url');
										}
										else {
											SendRequest(curr_file, 'post_file_upload', DOpus.strings.Get('msg_uploading_file'), 'https://www.virustotal.com/api/v3/files');
										}
									}
									else {
										Log(3, curr_file + DOpus.strings.Get('msg_file_not_found_VT'));
										dlg.report_btn.enabled = false;
										StopProcessing(curr_file, 'complete');
									}
								}
								catch (err) {
									Log(3, curr_file + ' => ' + req.id + '=> Error when reading blob : ' + err.description);
									StopProcessing(curr_file, 'error', DOpus.strings.Get('error_uploading_file'));
								}

							}
							else if (req.responsecode === 427) { //exceeded quota
								SendRequest(curr_file, 'get_remaining_quota', DOpus.strings.Get('msg_checking_quota'), 'https://www.virustotal.com/api/v3/users/' + api_key + '/overall_quotas');
							}
							else {
								StopProcessing(curr_file, 'error', 'Error ' + req.responsecode + ' : ' + req.response);
							}
							try {
								Log(1, curr_file + ' => Shutting down request ' + req.id);
								req.shutdown();
							}
							catch (err) {
								Log(1, '=> unable to shutdown connection or theres no connection available');
							}
							break;
						case 'error':
							try {
								Log(1, curr_file + ' => ' + req.id + '=> ' + req.errorcode + ' : ' + req.errortext);
								StopProcessing(curr_file, 'error', 'Error ' + req.errorcode + ' : ' + req.errortext);
							}
							catch (err) {
								Log(3, curr_file + ' => ' + req.id + '=> Error : ' + err.description);
								StopProcessing(curr_file, 'error', strings.error_reading_response);
							}
							break;
					}

				}
				else if (msg.event === 'drop' && ui_status === 'idle') {
					try {
						var new_files = GetFiles(msg.object, false, true);
						for (var i = new Enumerator(new_files); !i.atEnd(); i.moveNext()) {
							curr_file = new_files(i.item())('item');
							SetDlgFileInfo(curr_file);
							curr_time = 0;
							if (processed_files.Exists(curr_file)) SetInfo(curr_file);
							else {
								files.Set(curr_file, new_files(curr_file));
								total_files++;
								dlg.main.AddCustomMsg(curr_file.def_value, true);
								dlg.list_vendors.redraw = false;
								dlg.list_details.redraw = false;
								dlg.list_sandbox.redraw = false;
								dlg.list_sa.redraw = false;

								dlg.list_vendors.RemoveItem(-1);
								dlg.list_details.RemoveItem(-1);
								dlg.list_sandbox.RemoveItem(-1);
								dlg.list_sa.RemoveItem(-1);
								dlg.list_vendors.redraw = true;
								dlg.list_details.redraw = true;
								dlg.list_sandbox.redraw = true;
								dlg.list_sa.redraw = true;
								if (GetFileHash(curr_file)) {
									ui_status = 'processing';
									StartProgress();
									dlg.main.SetTimer(200, 'progress_bar');
									SetNotifyBanner(curr_file, strings.msg_get_hash, 'update');

								}
								else StopProcessing(curr_file, 'error', DOpus.strings.Get('error_reading_hash'));

							}
						}

					}
					catch (err) {
						Log(3, 'Unable to add dropped files : ' + err.description);
					}
				}
				else if (msg.event === 'timer') {
					if (msg.control === 'progress_bar') {
						if (dlg.progress_bar.visible) { //sending,hashing
							//still waiting for a response, redraw the progress bar
							curr_time += msg.data;
							// Log(1, 'timer : ' + (new Date() - ini_time) + '\tstatus : ' + status);
							max_bar_width = dlg.track_bar.x + dlg.track_bar.cx - 2;
							with(dlg.progress_bar) {
								if (x + cx + progress_step > max_bar_width) { //doesn't fit another step
									label = '';
									if (cx < progress_step) { //start from init
										x = bar_ini_x;
										cx = progress_width;
									}
									else { //keep moving and resizing the trackbar
										x = x + cx;
										cx = max_bar_width - x;
									}
								}
								else {
									x = x + progress_step;
									label = ~~(curr_time / 1000) + 's';
								}
							};
						}
					}
					else if (msg.control === 'update_vendors_timer') {
						dlg.main.KillTimer('update_vendors_timer');
						FilterListVendors(files(curr_file)('report').results);
					}
					else if (msg.control.indexOf('wait_timer_') === 0) {
						curr_file = msg.control.slice(11);
						files(curr_file)('wait_end_timer') = files(curr_file)('wait_end_timer') + 1;
						Log(1, curr_file + ' : ini_timer=' + files(curr_file)('wait_ini_timer') + '; end_timer=' + files(curr_file)('wait_end_timer'));
						dlg.banner.label = banner_text + strings.msg_waiting_label.replace('%s', files(curr_file)('wait_ini_timer') - files(curr_file)('wait_end_timer'));
						dlg.banner.AutoSize();
						if (files(curr_file)('wait_ini_timer') <= files(curr_file)('wait_end_timer')) {
							dlg.main.KillTimer(msg.control);
							if (files(curr_file)('wait_timer_lapse')) SendRequest(curr_file, 'get_analysis', strings.msg_check_exists_VT, files(curr_file)('endpoint'));

							else SendRequest(curr_file, 'get_report', strings.msg_check_exists_VT, 'https://www.virustotal.com/api/v3/files/' + files(curr_file)('hash'));
						}
					}
					else { //request timer
						Log(1, 'Timer ' + msg.control);
						dlg.main.KillTimer(msg.control);
						try {
							var req_id = msg.control.slice(8);
							Log(1, 'Attempt to kill request ' + req_id + ' for ' + req_map(req_id)('file'));
							req_map(req_id)('req').shutdown();
						}
						catch (err) {
							Log(3, 'unable to kill request ' + req_id + ' : ' + err.description);
						}
						StopProcessing(req_map(req_id)('file'), 'error', strings.error_reading_response);
					}
				}
				else if (msg.event === 'rclick' && msg.control === 'notifyicon') {
					var dlgMenu = DOpus.Dlg();
					dlgMenu.choices = [str_tools.LanguageStr(2012)];
					dlgMenu.menu = 0;
					var dlg_res = dlgMenu.Show();
					if (dlg_res == 1) {
						Log(2, 'User cancel the command...');
						dlg.main.EndDlg(0);
					}
				}
				else if (msg.event === 'click') {

					if (msg.control === 'report_btn') {
						var dlgMenu = DOpus.Dlg();
						dlgMenu.choices = report_menu;
						dlgMenu.menu = 0;
						var dlg_res = dlgMenu.Show();
						BuildReport(curr_file, dlg_res, files(curr_file)('raw_data'), files(curr_file)('text_data'));
					}
					else if (msg.control === 'cancel_btn') {
						dlg.main.EndDlg(0);
					}
					else if (msg.control === 'clear_btn') {
						dlg.search_vendors.value = '';
					}
					else if (files(curr_file)('link') && msg.control == 'banner') {
						if (msg.qualifiers === 'ctrl') {
							DOpus.SetClip(files(curr_file)('link'));
							SetNotifyBanner(curr_file, DOpus.strings.Get('msg_link_copied'), 'notify', script_name);
						}
						else {
							cmd.RunCommand(files(curr_file)('link'));
							dlg.main.EndDlg(1);
						}
					}
				}
				else if (msg.event === 'editchange' && msg.control === 'filter_vendors') {
					dlg.main.SetTimer((dlg.search_vendors.value !== '') ? 250 : 10, 'update_vendors_timer');
				}
			}
			if (!dlg.main.result) {
				for (var i = new Enumerator(req_map); !i.atEnd(); i.moveNext()) {
					try {
						req_id = i.item();
						req = req_map(req_id)('req');
						if (req && !req.complete && req.status != 'notready') {
							Log(1, 'Attempt to kill request ' + req_id + ' for ' + req_map(req_id)('file') + ' : ' + req_map(req_id)('status_id'));
							req.shutdown();
						}
					}
					catch (err) {
						Log(1, ' => Error trying to close request ' + req_id + ': ' + err.description);
					}
				}
			}
			req = null;
			req_map = null;
			cat_map = null;
		}
		else {
			MsgDlg(DOpus.strings.Get('msg_in_use'), 0, '', tab);
			Log(4, 'Another dialog is already opened!');
		}
		dlg = null;
		tab = null;
	}
	else {
		MsgDlg(DOpus.strings.Get('msg_no_file'), 0, '', scriptCmdData.func.sourcetab);
		Log(4, 'A file is needed to continue!');
	}
	if (has_clip) DOpus.SetClip(NOGUI === 1 ? (has_clip + '}') : has_clip);
	if (NOGUI) {
		if (!flagged_files.empty) ShowWarningDlg();
		else SetNotifyBanner('', '', 'notify', script_name + ' v' + script_version, DOpus.strings.Get('msg_no_threats'));
	}

	cmd = null;
	has_clip = null;
	FSU = null;
	str_tools = null;
	wild_obj = null;
	files = null;
	processed_files = null;

	Log(1, ' =========== COMMAND FINISHED =========== ');
	return;

	function ShowWarningDlg() {
		var dlg = {};
		dlg.main = scriptCmdData.func.Dlg();
		dlg.main.template = 'results';
		if (icoObj && icoObj.type) {
			dlg.main.icon = icoObj;
		}
		dlg.main.title = script_name + ' v' + script_version + ' - ' + DOpus.strings.Get('title_results_dlg');
		dlg.main.Create();
		dlg.listview = dlg.main.Control('listview');
		with(dlg.listview.columns) {
			GetColumnAt(0).name = DOpus.strings.Get('header_file'); //File
			GetColumnAt(1).name = DOpus.strings.Get('header_result'); //Result
		}
		for (var i = new Enumerator(flagged_files); !i.atEnd(); i.moveNext()) {
			var row = dlg.listview.getItemAt(dlg.listview.AddItem(i.item()));
			if (row) {
				row.subitems(0).text = flagged_files(i.item())('msg');
				if (flagged_files(i.item())('color')) row.subitems(0).fg = flagged_files(i.item())('color');
				row.subitems(1).text = files.Exists(i.item()) && files(i.item()).Exists('link') ? files(i.item())('link') : '';
			}
		}
		dlg.listview.columns.autosize();
		dlg.main.Control('banner').label = DOpus.strings.Get('title_warning_banner');
		var x_pos, y_pos;
		var sys_info = DOpus.Create().SysInfo();
		try {
			var area = sys_info.WorkAreas(sys_info.MouseMonitor);
			if (!area.empty) {
				x_pos = area.width - dlg.main.cx - 10;
				y_pos = area.height - dlg.main.cy - 10;

			}
			area = null;
		}
		catch (err) {
			Log(3, 'Error retrieving monitor info : ' + err.description);
		}
		sys_info = null;
		dlg.main.position = 'monitor';
		if (x_pos) dlg.main.x = x_pos;
		if (y_pos) dlg.main.y = y_pos;

		dlg.main.Show();
		var msg;
		while (true) {
			msg = dlg.main.GetMsg();
			if (!msg.result) break;
			if (msg.event === 'dblclk' && msg.name === 'listview') {
				try {
					var file = dlg.listview.value;
					cmd.RunCommand(msg.qualifiers === 'ctrl' ? ('Go "' + file.name + '" NEWTAB=tofront,findexisting') : row.subitems(1).text);
				}
				catch (err) {
					Log(3, ' Error running command!');
				}
			}
		}

		dlg = null;
	}

	function FilterListVendors(resultsObj) {
		Log(1, 'Filtering Vendors...');
		if (!resultsObj) return;
		dlg.list_vendors.redraw = false;
		try {
			dlg.list_vendors.RemoveItem(-1);
			var query = dlg.search_vendors.value.trim();
			if (query) wild_obj.parse('*' + query + '*', '');
			var row, vendor_name, vendor_cat, vendor_result;
			for (var key in resultsObj) {
				vendor_name = resultsObj[key].engine_name;
				vendor_cat = cat_map(resultsObj[key].category);
				vendor_result = resultsObj[key].result || '';
				if (!query || wild_obj.match(vendor_name) || wild_obj.match(vendor_cat) || wild_obj.match(vendor_result)) {
					row = dlg.list_vendors.getItemAt(dlg.list_vendors.AddItem(vendor_name));
					if (row) {
						row.subitems(0) = resultsObj[key].engine_update;
						row.subitems(1) = vendor_cat;
						row.subitems(2) = vendor_result || '---';
					}
				}
			}
		}
		catch (err) {
			Log(3, 'Error when filtering vendors list : ' + err.description);
		}
		dlg.list_vendors.redraw = true;
		return;
	}

	function SetNotifyBanner(file, message, notify_action, notify_title, notify_message) {
		if (!notify_title) {
			dlg.banner.label = message;
			dlg.main.NotifyIcon(notify_action, dlg.main.icon, script_name + ' v' + script_version + '\n' + file + ' : ' + (notify_message || message));
		}
		else dlg.main.NotifyIcon(notify_action, notify_title, file + ' : ' + (notify_message || message), 'n');
	}

	function SetWaitTimer(file, timelapse) {
		files(file)('wait_ini_timer') = timelapse ? timelapse : (60 - DOpus.Create().Date().sec);
		files(file)('wait_end_timer') = 0;
		Log(1, 'Init waiting timer...' + files(file)('wait_ini_timer'));
		files(file)('wait_timer_lapse') = timelapse;
		dlg.main.SetTimer(1000, 'wait_timer_' + file);
	}

	function SetVars(file, date, statsObj, resultsObj, data) {
		files(file).Set('report', {});
		files(file)('report').date = date ? date : null;
		files(file)('report').stats = statsObj ? statsObj : null;
		files(file)('report').results = resultsObj ? resultsObj : null;
		files(file)('report').data = data ? data : null;
		files(file).Set('raw_data', raw_data ? raw_data : null);
	}

	function SetInfo(file, timestamp, statsObj, resultsObj, data) {
		Log(1, file + ' : Setting info...');
		var text_data = {};
		text_data.basics = {};
		text_data.basics[str_tools.LanguageStr(10)] = file;
		text_data.basics['URL'] = files(file)('link');
		text_data.basics[DOpus.strings.Get('label_size')] = file.size.fmt;
		var timestamp = files(file)('report').date;
		if (timestamp) {
			var date = DOpus.Create().Date('1970-01-01');
			date.Add(timestamp, 's');
			dlg.file_info_date.label = date.FromUTC().Format();
		}
		else dlg.file_info_date.label = '';
		text_data.basics[DOpus.strings.Get('label_analyzed')] = dlg.file_info_date.label;
		text_data.summary = {};
		//fill results
		var statsObj = files(file)('report').stats;
		if (statsObj && typeof statsObj === 'object') {
			text_data.summary[DOpus.strings.Get('label_summary')] = 'is_subtitle';
			var total_vendors = 0;
			var color;
			if (typeof statsObj === 'object') {
				total_vendors += statsObj['confirmed-timeout'];
				total_vendors += statsObj.failure;
				total_vendors += statsObj.harmless;
				total_vendors += statsObj.malicious;
				total_vendors += statsObj.suspicious;
				total_vendors += statsObj.timeout;
				total_vendors += statsObj['type-unsupported'];
				total_vendors += statsObj.undetected;

				dlg.stats1.label = '<b><#%vs_listview_header_text>' + cat_map('total') + ' : </#></b>' + total_vendors + '\n<b><#%vs_listview_header_text>' + cat_map('timeout') + ' : </#></b>' + (statsObj.timeout + statsObj['confirmed-timeout']);
				dlg.stats2.label = '<b><#%vs_listview_header_text>' + cat_map('harmless') + ' : </#></b>' + statsObj.harmless + '\n<b><#%log_error>' + cat_map('failure') + ' : </#></b>' + statsObj.failure;
				dlg.stats3.label = '<b><#%log_error>' + cat_map('suspicious') + ' : </#></b>' + statsObj.suspicious + '\n<b><#%log_error>' + cat_map('malicious') + ' : </#></b>' + statsObj.malicious;
				dlg.stats4.label = '<b><#%vs_listview_header_text>' + cat_map('type-unsupported') + ' : </#></b>' + statsObj['type-unsupported'] + '\n<b><#%log_ftp_data_in>' + cat_map('undetected') + ' : </#></b>' + statsObj.undetected;
				for (var e = new Enumerator(cat_map); !e.atEnd(); e.moveNext()) {
					if (e.item() == 'confirmed-timeout') continue;
					text_data.summary[cat_map(e.item())] = e.item() in statsObj ? (e.item() == 'timeout' ? (statsObj[e.item()] + statsObj['confirmed-timeout']) : statsObj[e.item()]) : total_vendors;
				}
				e = null;
				if (statsObj.malicious)
					flagged_files.Set(file, DOpus.NewMap('color', '#%log_error', 'msg', strings.msg_possible.replace('%1', cat_map('malicious')) + ' (' + statsObj.malicious + '/' + (total_vendors - statsObj['type-unsupported']) + ')'));
				else if (statsObj.suspicious)
					flagged_files.Set(file, DOpus.NewMap('color', '#%log_error', 'msg', strings.msg_possible.replace('%1', cat_map('suspicious')) + ' (' + statsObj.suspicious + '/' + (total_vendors - statsObj['type-unsupported']) + ')'));
				else if (statsObj.failure + statsObj.timeout + statsObj['confirmed-timeout'] >= statsObj.undetected)
					flagged_files.Set(file, DOpus.NewMap('color', '#%vs_dragdrop_warning_action', 'msg', cat_map('failure') + '/' + cat_map('timeout') + ' (' + (statsObj.failure + statsObj.timeout + statsObj['confirmed-timeout']) + '/' + (total_vendors - statsObj['type-unsupported']) + ')'));
				else if (FORCE_NOTIFY) {
					if (statsObj.undetected > statsObj.harmless)
						flagged_files.Set(file, DOpus.NewMap('color', '', 'msg', cat_map('undetected') + ' (' + statsObj.undetected + '/' + (total_vendors - statsObj['type-unsupported']) + ')'));
					else flagged_files.Set(file, DOpus.NewMap('color', '', 'msg', cat_map('harmless') + ' (' + statsObj.harmless + '/' + (total_vendors - statsObj['type-unsupported']) + ')'));
				}

			}
			else text_data.summary['!'] = statsObj;
		}
		else {
			dlg.stats1.label = '';
			dlg.stats2.label = '';
			dlg.stats3.label = '';
			dlg.stats4.label = '';
			flagged_files.Set(file, DOpus.NewMap('color', '', 'msg', statsObj || '???'));
			text_data.summary[statsObj || '???'] = 'is_subtitle';
		}
		dlg.list_vendors.redraw = false;
		dlg.list_vendors.RemoveItem(-1);
		var resultsObj = files(file)('report').results;
		if (resultsObj) { //fill listviewer
			text_data.vendors = {};
			text_data.vendors[DOpus.strings.Get('label_vendors')] = 'is_subtitle';

			var row;
			for (var key in resultsObj) {
				row = dlg.list_vendors.GetItemAt(dlg.list_vendors.AddItem(resultsObj[key].engine_name));
				if (row) {
					row.subitems(0) = resultsObj[key].engine_update;
					row.subitems(1) = cat_map(resultsObj[key].category);
					row.subitems(2) = resultsObj[key].result == null ? '---' : resultsObj[key].result;
					text_data.vendors[row.name] = row.subitems(0) + '\t\t' + row.subitems(1) + '\t\t' + row.subitems(2);
					if (!NOGUI) {
						switch (resultsObj[key].category) {
							case 'malicious':
							case 'suspicious':
								row.subitems(2).fg = row.subitems(1).fg = '#%log_error';
								break;
							case 'failure':
							case 'timeout':
							case 'confirmed-timeout':
								row.subitems(2).fg = row.subitems(1).fg = '#%vs_dragdrop_warning_action';
								break;
						}
					}
				}
			}

		}
		dlg.list_vendors.columns.AutoSize();
		dlg.list_vendors.columns.GetColumnAt(3).sort = -1;
		dlg.list_vendors.redraw = true;

		dlg.list_details.redraw = false;
		dlg.list_sandbox.redraw = false;
		dlg.list_sa.redraw = false;
		dlg.list_details.RemoveItem(-1);
		dlg.list_sandbox.RemoveItem(-1);
		dlg.list_sa.RemoveItem(-1);
		dlg.group_sandbox.label = DOpus.strings.Get('label_sandbox_verdicts');
		dlg.group_sa.label = DOpus.strings.Get('label_sigma_analysis_summary');
		var data = files(file)('report').data;
		if (data) {
			text_data.report = {};

			text_data.report[DOpus.strings.Get('label_report')] = 'is_subtitle';
			try {
				var row, val;
				for (var i = 0; i < reports_set.length; i++) {
					if (reports_set(i) in data.attributes) {
						val = data.attributes[reports_set(i)];
						Log(1, '\t' + reports_set(i) + ' : ' + data.attributes[reports_set(i)]);
						if (reports_set(i).slice(-4) === 'date') {
							var date2 = DOpus.Create().Date('1970-01-01');
							date2.Add(val, 's');
							val = date2.FromUTC().Format();
						};
						row = dlg.list_details.GetItemAt(dlg.list_details.AddItem(DOpus.strings.Get('label_' + reports_set(i))));
						if (row) {
							row.subitems(0) = val;
							text_data.report[row.name] = val;
						}
					}

				}
				if (data.attributes.reputation) {
					row = dlg.list_details.GetItemAt(dlg.list_details.AddItem(DOpus.strings.Get('label_reputation')));
					if (row) {
						row.subitems(0) = data.attributes.reputation;
						text_data.report[row.name] = row.subitems(0).text;
					}
				}
				if ('signature_info' in data.attributes) {
					row = dlg.list_details.GetItemAt(dlg.list_details.AddItem(DOpus.strings.Get('label_signed')));
					if (row) {
						row.subitems(0) = (data.attributes.signature_info.verified === 'Signed' ? DOpus.strings.Get('label_yes') : DOpus.strings.Get('label_no')) +
							(data.attributes.signature_info['signing date'] ? (' (' + data.attributes.signature_info['signing date'] + ')') : '');
						text_data.report[row.name] = row.subitems(0).text;
					}
				}

				text_data.report[DOpus.strings.Get('label_sandbox_verdicts')] = 'is_subtitle';
				if ('sandbox_verdicts' in data.attributes) {
					for (var key in data.attributes.sandbox_verdicts) {
						row = dlg.list_sandbox.GetItemAt(dlg.list_sandbox.AddItem(data.attributes.sandbox_verdicts[key]['sandbox_name']));
						if (row) {
							row.subitems(0) = cat_map(data.attributes.sandbox_verdicts[key]['category']);
							text_data.report[row.name] = row.subitems(0);
						}
					}
				}
				else dlg.list_sandbox.AddItem(DOpus.strings.Get('msg_not_available'));

				text_data.report[DOpus.strings.Get('label_sigma_analysis_summary')] = 'is_subtitle';
				if ('sigma_analysis_summary' in data.attributes) {
					var c = {
						high: 0,
						medium: 0,
						critical: 0,
						low: 0
					};
					for (var key in data.attributes.sigma_analysis_summary) {
						for (var p in c) {
							c[p] += data.attributes.sigma_analysis_summary[key][p];
						}
					}
					for (var key in c) {
						row = dlg.list_sa.GetItemAt(dlg.list_sa.AddItem(DOpus.strings.Get('label_' + key)));
						if (row) {
							row.subitems(0) = c[key];
							text_data.report[row.name] = row.subitems(0);
						}
					}

					c = null;

				}
				else dlg.list_sa.AddItem(DOpus.strings.Get('msg_not_available'));

			}
			catch (err) {
				Log(1, ' => Error : ' + err.description);
			}
		}
		files(file).Set('text_data', text_data);
		dlg.list_details.columns.AutoSize();
		dlg.list_sandbox.columns.AutoSize();
		dlg.list_sa.columns.AutoSize();
		dlg.list_details.redraw = true;
		dlg.list_sandbox.redraw = true;
		dlg.list_sa.redraw = true;
	}

	function StopProcessing(file, status, msg, response, raw_data) {
		Log(1, 'Stop processing ' + file + '(' + DOpus.TypeOf(file) + ')');
		if (status === 'error') {
			banner_text += '\n<#%log_error>' + msg + '</#>';
			files(file)('link') = '';
			SetVars(file);
			flagged_files.Set(file, DOpus.NewMap('color', '#%log_error', 'msg', msg));
		}
		else {
			files(file)('link') = msg || '';
			if (files(file)('link') && scriptCmdData.func.args.got_arg.URLTOCLIP) DOpus.SetClip(files(file)('link'));
			if (response && raw_data) SetVars(file, response.data.attributes.last_analysis_date || response.data.attributes.date, response.data.attributes.last_analysis_stats || response.data.attributes.stats, response.data.attributes.last_analysis_results || response.data.attributes.results, response.data, raw_data);
			else SetVars(file, Math.floor(new Date().getTime() / 1000), banner_text);
		}

		SetInfo(files(file)('item'));

		processed_files.insert(file);
		Log(1, 'processed_files=' + processed_files.size + ' : total_files=' + total_files);
		if (NOGUI)
			BuildReport(file, NOGUI, files(file)('raw_data'), files(file)('text_data'));
		try {
			if (processed_files.size >= total_files) {
				Log(1, 'All files has been processed!');
				dlg.main.KillTimer('progress_bar');
				ui_status = 'idle';
				dlg.track_bar.title = DOpus.strings.Get('msg_total_time') + ' : ' + (curr_time / 1000) + 's';
				dlg.track_bar.bg = '#!BTNFACE';
				dlg.track_bar.fg = '#!BTNTEXT';
				dlg.progress_bar.visible = false;
				dlg.search_vendors.enabled = true;
				dlg.search_btn_vendors.enabled = true;
				dlg.clear_btn_vendors.enabled = true;
				dlg.close_btn.label = str_tools.LanguageStr(631); //close
				SetNotifyBanner(file, files(file)('link') ? (DOpus.strings.Get('msg_more_info') + ' :\n<a id="link">' + files(file)('link') + '</a>') : banner_text, 'update', '', strings[status]);
				dlg.report_btn.visible = status == 'complete';
				if (NOGUI) dlg.main.EndDlg(1);
			}
			else {
				Log(1, 'Passing to next file...');
				files_enum.moveNext();
				if (!files_enum.atEnd()) {
					var next_file = files(files_enum.item())('item');
					dlg.main.AddCustomMsg(next_file.def_value, true);
					if (GetFileHash(next_file)) SetNotifyBanner(next_file, strings.msg_get_hash, 'update');
					else StopProcessing(next_file, 'error', DOpus.strings.Get('error_reading_hash'));
				}
			}
		}
		catch (err) {
			Log(3, 'Error processing stop for ' + file + ' : ' + err.description);
			dlg.main.EndDlg(0);
		}
	}

	function SendRequest(file, id, text, endpoint) {
		try {
			if (endpoint) {
				var req = dlg.main.NewHTTPReq();

				// req_map(pos).req = dlg.main.NewHTTPReq();
				Log(1, file + ' => Sending request "' + req.id + '" for ' + id + ' to ' + endpoint);
				req.AddHeader('x-apikey', api_key);
				req.AddHeader('accept', 'application/json');
				req.SetTimeout(max_timeout);

				if (id == 'post_file_upload')
					req.AddPostData('file', file.Open().Read(), 'application/x-msdownload', file.name);
				req.SendRequest(endpoint);
				Log(1, file + ' => Request "' + req.id + '" sent for ' + id);
				SetNotifyBanner(file, text, 'update');
				req_map.Set(req.id, DOpus.NewMap('file', file, 'req', req, 'status_id', id));
				files(file).Set('status_id', id);
				if (max_timeout) dlg.main.SetTimer(max_timeout * 1000 + 1000, 'request_' + req.id);
			}
		}
		catch (err) {
			Log(3, '=> Error when sending request : ' + err.description);
			StopProcessing(file, 'error', DOpus.strings.Get('error_sending_request'));
		}
	}

	function GetFileHash(file) {
		if (!file) return false;
		// Log(2, "Getting sha256 checksum for " + file);
		var cmdline = 'GetHash "' + file + '" HASH=sha256';
		Log(1, 'Sending hash : ' + cmdline);
		return cmd.RunCommandAsync(cmdline);
	}

	function BuildReport(file, option, raw_data, data) {
		switch (option) {
			case 1: //json,toclip
				if (raw_data) {
					if (NOGUI) {
						if (!has_clip) has_clip += '{';
						else has_clip += ',\n';
						has_clip += '"' + String(file).replace(/\\/g, '\\\\') + '":' + raw_data;
					}
					else DOpus.SetClip(raw_data);
				}
				else Log(3, file + ' : No raw data available');
				break;
			case 2: //json,tofile
				if (raw_data) WriteReport(file, raw_data, 'json');
				else Log(3, file + ' : No raw data available');
				break;
			case 3: //txt,toclip
				if (data) {
					if (NOGUI) {
						if (has_clip) has_clip += '\n\n' + Array(100).join('#') + '\n\n';
						has_clip += ObjToText(data);
					}
					else DOpus.SetClip(ObjToText(data));
				}
				break;
			case 4: //txt,tofile
				if (data) WriteReport(file, ObjToText(data), 'txt');
				break;
		}
	}

	function ObjToText(obj) {
		var text = '';
		for (var key in obj) {
			if (text) text += '\n\n';
			for (var sub_key in obj[key])
				text += sub_key + (obj[key][sub_key] === 'is_subtitle' ? '' : (PT(sub_key.length) + ': \t\t' + obj[key][sub_key])) + '\n';

		}
		return text.slice(0, -1);

		function PT(length) {
			var pt = '';
			for (; length < 25; length++)
				pt += ' ';
			return pt;
		}
	}

	function WriteReport(file, data, mode) {
		Log(2, file + ' : Saving report as ' + mode);
		var out_file = file + '_VTReport.' + mode;
		var c = 0;
		while (c <= 1) {
			var output = FSU.OpenFile(out_file, 'wfXX');
			if (output.error != 0 && c == 0) {
				Log(3, 'Error opening ' + out_file + ' for writing:' + output.error + '. Let the user choose the file then');
				out_file = DOpus.Dlg().Save(script_name + ' - Choose location for report', '', dlg.main, mode.toUpperCase() + '!*.' + mode);
				if (out_file.result == false) c = 2;
				c++;
			}
			break;
		}
		if (c > 1) {
			MsgDlg('Unable to save report!', '', '', dlg.main);
			return;
		}
		try {
			output.Write(data);
			output.Close();
		}
		catch (err) {
			Log(3, 'Error when writing data in ' + out_file + ':' + err.description);
		}
	}

	function StartProgress() {
		//==== SET PROGRESS BAR ============
		dlg.progress_bar.visible = true;
		dlg.progress_bar.cy = dlg.track_bar.cy - 4;
		dlg.progress_bar.x = dlg.track_bar.x + 2;
		dlg.progress_bar.y = dlg.track_bar.y + 2;
		dlg.progress_bar.cx = progress_width;
		dlg.track_bar.title = '';
		dlg.track_bar.bg = '#%vs_progress_background';
		dlg.track_bar.fg = '#%jobsbar_text';
		dlg.progress_bar.fg = '#%jobsbar_text';
		dlg.progress_bar.bg = '#%vs_progress_bar_partial';
		dlg.progress_bar.style = 'b';

		dlg.search_vendors.enabled = false;
		dlg.search_btn_vendors.enabled = false;
		dlg.clear_btn_vendors.enabled = false;

		dlg.close_btn.label = str_tools.LanguageStr(2012); //cancel

	}

	function SetDlgFileInfo(curr_file) {
		try {
			var thumb = DOpus.LoadThumbnail(curr_file, 2000, '', '', 'i');
			if (thumb) dlg.main.Control('file_icon').label = thumb;
			thumb = null;
		}
		catch (err) {
			Log(1, 'Unable to load thumbnail for ' + curr_file);
		}
		dlg.main.title = script_name + ' v' + script_version + ' - ' + curr_file.name;
		dlg.file_name.label = curr_file.def_value;
		dlg.file_info_size.label = curr_file.size.fmt;

	}

	function GetIcoObj() {
		try {
			var base64_ico = 'AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAKBEAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKahcMiypX' +
				'rgsql60KqpgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkkkkHLKpfhCyqXfssq17jLKpe5CyqXvorqV2DKqpVBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqlUGLKpeuSyqXvwsq' +
				'l6QJ7FiDSedYg0rql6TLape/CypXrgqqlUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALKteqiyqXv0sql5XAAAAAAAAAAAAAAAAAAAAACyqXlcsql79LKteqQAAAAAAAAAAAAAAAAAAAAAAAAAALapeWiyqXv8sql9pAAA' +
				'AAAAAAAArqV1lLKleYgAAAAAAAAAAK6xdayyqXv8rq2BYAAAAAAAAAAAAAAAAKqpVDCupXuUsql7BKqpbKiyrXp0sqV9WLKpe4iyqXuEsrF9WLKleniyoXSksql7DLKpe4y6iXQsAAAAAAAAAACupXWssql7/LapfMyexYg0' +
				'sql7xLKpe/yyqXv8sql7/LKpe/yyqXfAnnWINK6hgNSyqXv8rqF5qAAAAAAAAAAAsql7JK6pezgAAAAAqql42LKte9SyqXv8sql7/LKteniuqXqUsql71KqpeNgAAAAAsql7PK6pdyAAAAAAzqlUPLKte/iypXnosql5XLKp' +
				'e/yyqXv8tql7lLKperSyqXv8sql7/LKpe/yyqXv8tq11VK6tffCyqXv0nnWINK6leQSyqXv8qp19DAAAAACupWzssql75LKperiuoYDUsql7/LKpe/yyqXvgtql45AAAAACyqYEUsql7/La1fPiypXmIsql7/KqpdHgAAAAAq' +
				'qlUGLKpe7SyqXv8sql7/LKpe/yyqXv8rql7sKqpVBgAAAAAprVofLKpe/yqqXWAsrF1uLKpe/yamWRQAAAAALKxeLiyqXboqql5yLKle7iyqXu0tql5yLKpfui2qYC0AAAAAJrNmFCyqXv8sq15tLapeOSyqXv0rql7lLKleei' +
				'amWRQAAAAAAAAAACyrX3krq112AAAAAAAAAAAmplkULKpdey2qX+Usql79LqhgOAAAAAAtql0/LKpetCyqXv0sql72K6pdmSyuXSkAAAAAAAAAACqqWyorql2ZLKpe9iyqXv0sql+yLalfPgAAAAAAAAAAAAAAAAAAAAAsqF8jK' +
				'6tdjiyrXu8sql7+LKleuCypXrgsql7+LKpe7yupXY4sqF8jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuol0LLKldaCyqXtMsql7TLKldaC6iXQsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAAAAAAsqlxFK6lemyupXZQtql0/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAkqmEVK6pesSyqXv8sql7/LKpe/yyqXv8rql6rJqZZFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAKqleXCuqXu0sql7/LKpe/yyqXv8sql7/LKpe/yyqXv8rql7sKqpeWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6dYGiyqXr' +
				'Qsql7/LKpe/yyqXv8sql7/KqpekCupXZQsql7/LKpe/yyqXv8sql7/K6ldsiOuXRYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACynYR0sql3pLKpe/y' +
				'yqXv8sql7/K6pezyysXTQAAAAAAAAAACmoXDIrql3VLKpe/yyqXv8sql7/K6le5SynYR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjrl0WLKpd3SyqXv8sql7/K' +
				'6pd8yurXXAAgIACAAAAAAAAAAAAAAAAAAAAAACqVQMsqVx0LKld9CyqXv8sql7/LKpd3SOuXRYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIK9gECuqXtQsql7/LKpe/yuqXvIr' +
				'qFwvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsrF4uK6pd8yyqXv8sql7/K6pe1CCvYBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAEsql7GLKpe/yyqXv8rql74KKpeOQAA' +
				'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArqV87K6pe+CyqXv8sql7/K6pdwgD/AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALKpcaSyqXv8sql7/LKle+iqrW0MAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAACypXoAsql17AAAAAAAAAAAAAAAAAAAAAAAAAAArqV1HLKpd/CyqXv8sql7/K6teZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2lWhEsql7vLKpe/yyqXv8qq15nAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAqqmAYLKpd/CyqXfwtpVoRAAAAAAAAAAAAAAAAAAAAAAAAAAArq15qLKpe/yyqXv8rql7sIK9gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK6lelSyqXv8sql7/LKldzQD/AAEop2AgLKpdey2lWhEAAAA' +
				'AAAAAACypXYwsql7/LKpe/yupXogAAAAAAAAAACqqVRIsql17KKdgIAD/AAEsql3SLKpe/yyqXv8sqV2RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuoXC8sql7/LKpe/yyqXv8oql45AAAAACypXoUsql7/LKpe6iupXZQ' +
				'sql3FLKpe/yyqXv8sql7/LKpe/yyqXsYrql6TK6ld6yyqXv8rql2BAAAAACupXzssql7/LKpe/yupXv0pqF0sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK6lepiyqXv8sql7/LKldpAAAAAAAAAAAKKpfMyyqXv8sql7/LKpe/yyq' +
				'Xv8sql7/LKpe/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/ymoXDIAAAAAAAAAACyqXqgsql7/LKpe/yupXaEAAAAAAAAAAAAAAAAAAAAAAAAAACKqVQ8rql74LKpe/yyqXv8sqF0pAAAAAAAAAAAAAAAALKlexyyqXv8sql7/LKpe' +
				'/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/yyqXv8rql3CAAAAAAAAAAAAAAAAKqxfKyyqXv8sql7/K6pe+CexYg0AAAAAAAAAAAAAAAAAAAAAK6teZCyqXv8sql7/LKpezAAAAAAAAAAAAAAAAAD/AAEsql3YLKpe/yyqXv8sql7' +
				'/LKpe/yyqXv8sql7/KqtcPSmpXUosql7/LKpe/yuqXdUA/wABAAAAAAAAAAAAAAAAK6peziyqXv8sql7/KqtcYQAAAAAAAAAAAAAAAAAAAAArql3CLKpe/yyqXv8qql5sAAAAAAAAAAAqrF8rK6pdrSyqXv8sql7/LKpe/yyqXv' +
				'8sql7/LKpe/yyqXv8pqV8+LKpcSyyqXv8sql7/LKpe/yyqXaoqrF8rAAAAAAAAAAArq11wLKpe/yyqXv8sql2/AAAAAAAAAAAAAAAAJLZJByyqXfwsql7/LKpe/yqqVRIAAAAALKperiyqXv8sql7/LKpe/yyqXv8sql7/LKpe/y' +
				'yqXv8sql7/LKpe/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/yyqXv8rqV2sAAAAACOuXRYsql7/LKpe/yupXvcAgIACAAAAAAAAAAArqFs1LKpe/yyqXv8sql7XAAAAAAAAAAAsql6uLKpe/yyqXv8sql7/LKpe/yyqXv8sqV2XK' +
				'qpdHiypXZcsql7/LKpe/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/yypXqkAAAAAAAAAACupXtwsql7/LKpe/yqqWjAAAAAAAAAAACypXWgsql7/LKpe/yupXqAAAAAAAAAAAAAAAAAoqlstLKldtSyqXv8sql7/LKpe/yqqXR4A' +
				'AAAALKdhHSyqXv8sql7/LKpe/yyqXv8sql7/LKpe/yuqXrMqqlsqAAAAAAAAAAAAAAAAK6lepiyqXv8sql7/LKpdYwAAAAAAAAAAK6lemyyqXv8sql7/K6teagAAAAAAAAAAAAAAAAAAAAAcqlUJK6pd5iyqXv8sql7/K6lemyS' +
				'tWxwrqV6bLKpe/yyqXv8sql7/LKpe/yyqXv8sql3jJLZJBwAAAAAAAAAAAAAAAAAAAAAsql5vLKpe/yyqXv8qql6WAAAAAAAAAAAsqV67LKpe/yyqXv8pqV5EAAAAAAAAAAAAAAAAAAAAAAAAAAArqV6+LKpe/yyqXv8sql7/LKp' +
				'e/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/yuqXrkAAAAAAAAAAAAAAAAAAAAAAAAAACqqXEgsql7/LKpe/yuqXbcAAAAAAAAAACypXc0sql7/LKpe/ymoXDIAAAAAAAAAAAAAAAAAAAAAKa1cGSuqXfksql7/LKpe/yyqXv8sql7' +
				'/LKpe/yyqXv8sql7/LKpe/yyqXv8sql7/K6le9yymWRcAAAAAAAAAAAAAAAAAAAAAK6hbNSyqXv8sql7/K6ldygAAAAAAAAAAK6pd2yyqXv8sql7/Ka1aHwAAAAAAAAAAAAAAAAAAAAAsq15/LKpe/yyqXv8rql7PLKpd6SyqXv8' +
				'sql7/LKpe/yyqXv8sql3pLKpe0SyqXv8sql7/K6tdfAAAAAAAAAAAAAAAAAAAAAAop2AgLKpe/yyqXv8rql7aAAAAAAAAAAAsql3eLKpe/yyqXv8rqFwvAAAAAAAAAAAAAAAAAAAAACyrXDorqV2sKqtcPQAAAAAgr2AQK6peuSyq' +
				'Xv8sql7/LKldtSKqVQ8AAAAAKalfPiuqXqsoql45AAAAAAAAAAAAAAAAAAAAACqsXjEsql7/LKpe/yuqXtoAAAAAAAAAACupXbIsql7/LKpe/yuqXfksqV6dKqpbKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrGAoLKpe' +
				'/yyqXv8qqlwkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiqWy0sqV6dLKle+iyqXv8sql7/LKpergAAAAAAAAAAKKpfMyuqXvgsql7/LKpe/yyqXv8sql7/K6pdvSupXU0A/wABAAAAAAAAAAAAAAAAAAAAAAAAAAArql68' +
				'LKldtQAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AASqqX04rqV6+LKpe/yyqXv8sql7/LKpe/yyqXfYpqFwyAAAAAAAAAAAAAAAAKKpeOSuqXcIsql7/LKpe/yyqXv8sql7/LKpe/yuqXtorqV1rGrNmCgAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAAAAAGrNmCiupXWsrql3bLKpe/yyqXv8sql7/LKpe/yyqXv8sqV7BKqxdNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqWjArqV6gK6pe+CyqXv8sql7/LKpe/yyqXv8sqV3uK6ldiSStWxwAAAAAAAAAAAAAAAA' +
				'AAAAAJK1bHCqqXoosql7vLKpe/yyqXv8sql7/LKpe/yupXvcsqV6eKKpbLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqlUSLKleeiyqXeMsql7/LKpe/yyqXv8sql7/LKpd+yyqXqgpqFs4KahbOCyp' +
				'Xqksql37LKpe/yyqXv8sql7/LKpe/yypXuIqql14KqpVEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpVAyqqXlQsql3ALKpe/yyqXv8sql7/LKpe/yyqXv8sql7/LKpe/y' +
				'yqXv8sql7/LKpe/yyqXb8sq11SAKpVAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqlsqK6ldmiyqXvUsql7/LKpe/yyqXv8sql7/LKp' +
				'e9SupXZoqqlsqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIK9gECyqXnUrqV7ZLKpd2CyqXnUgr2AQA' +
				'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
				'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
			return DOpus.LoadImage(str_tools.Decode(base64_ico, 'base64'), '.ico');
		}
		catch (err) {
			Log(3, 'Error decoding ico : ' + err.description);
		}
		return null;
	}
}

function GetFiles(files, NOGUI, from_drop) {
	var map = DOpus.Create().OrderedMap();
	if (files) {
		for (var i = 0; i < files.count; i++) {
			var file = files(i);
			if (!file) continue;
			if (typeof file === 'string' || from_drop) file = FSU.GetItem(FSU.Resolve(file));
			Log(2, 'FILE       : ' + file + ':' + DOpus.TypeOf(file));
			if (!FSU.Exists(file)) {
				Log(3, '=> ' + file + ' can\'t be located!');
				continue;
			}
			if (file.is_dir) {
				Log(3, '=> ' + file + ' can\'t be a folder!');
				continue;
			}
			if (file.size > 681574400) { //file bigger than 650mb
				Log(3, '=> ' + file + ' is bigger than 650 mb, which is the size limit!');
				continue;
			}
			else if (file.size === 0) {
				Log(3, '=> ' + file + ' seems invalid!');
				continue;
			}
			Log(2, file + ' : size=' + file.size.fmt);
			map.Set(file, DOpus.NewMap('item', file, 'link', '', 'hash', 0));
			if (!NOGUI) break;
		}
	}
	return map;
}

function GetAPIKey() {
	if (!Script.vars.Exists('api-key')) return null;
	try {
		var key = str_tools.Decode(str_tools.Decode(Script.vars.Get('api-key'), 'base64'), 'utf-8');
	}
	catch (err) {
		Log(4, 'Unable to retrieve the api-key!');
		return null;
	}
	DOpus.vars.Set('VT_helper', Script.vars.Get('api-key'));
	DOpus.vars('VT_helper').persist = true;
	return key;
}

function SetAPIKey(dlg, key) {
	Log(2, 'Setting API-key...');
	if (!key) {
		var key = dlg.GetString(DOpus.strings.Get('msg_set_apikey'), '', '', '', DOpus.strings.Get('title_set_apikey'));
		if (typeof key == 'string') key = key.trim();
	}
	if (key != '') {
		try {
			key = str_tools.Encode(key, 'base64');
			Script.vars.Set('api-key', key);
			Script.vars('api-key').persist = true;
			Log(2, 'API key sucessfully stored. You can now use this command!');
		}
		catch (err) {
			Log(4, 'Error storing api-key : ' + err.description);
		}
	}
	// else Log(3, 'You have to enter your API key in order to use this command!');
	return;
}

function DelAPIKey(arg) {
	if (arg !== 'quiet' && MsgDlg(DOpus.strings.Get('msg_del_apikey'), 2, '&Yes|&No') == 0) return;
	Script.vars.Delete('api-key');
	Log(2, 'API key deleted!');
	return;
}

function MsgDlg(message, level, buttons, parent) {
	var dlg = DOpus.Dlg();
	if (parent) {
		dlg.window = parent;
		dlg.disable_window = parent;
	}
	dlg.message = message;
	dlg.buttons = buttons ? buttons : '&OK';
	dlg.title = script_name + ' v' + script_version;
	if (level === 0) dlg.icon = 'info';
	else if (level === 1) dlg.icon = 'question';
	else if (level === 2) dlg.icon = 'warning';
	else dlg.icon = 'error';
	dlg.Show();
	var s = dlg.result;
	dlg = null;
	return s;
}

function Log(level, text) {
	if (level === 4 || Script.config['log level'] < level) {
		if (level == 1) DOpus.Output('<#%vs_dragdrop_normal_action>DEBUG   => ' + text + '</#>');
		else if (level == 2) DOpus.Output('INFO    => ' + text);
		else if (level === 3) DOpus.Output('<#%vs_dragdrop_warning_action>WARNING => ' + text + '</#>');
		else DOpus.Output('ERROR   => ' + text, true);
	}
}
// Notifies a script it is being deleted through the Scripts management dialog
function OnDeleteScript(deleteScriptData) {
	DeleteVars();
}

function DeleteVars() {
	if (Script.vars.Exists('api-key') && !DOpus.vars.Exists('VT_helper')) {
		DOpus.vars.Set('VT_helper', Script.vars.Get('api-key'));
		DOpus.vars('VT_helper').persist = true;
	}
	Script.vars.Delete('*');
}

function CheckNewVT() {
	var scripts_folder = FSU.Resolve('/scripts');
	scripts_folder.Add('VTCommand.osp');
	return FSU.Exists(scripts_folder);
}

function UninstallScript() {
	DeleteVars();
	var cmd = DOpus.Create().Command();
	cmd.AddFile(Script.file);
	cmd.RunCommand('DELETE QUIET FORCE');
}
String.prototype.trim = function() {
	return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}

==SCRIPT RESOURCES
<resources>
	<resource name="main" type="dialog">
		<dialog dragdrop="yes" height="266" lang="english" maximize="yes" minimize="yes" resize="yes" width="302">
			<control halign="center" height="32" image="yes" name="file_icon" type="static" valign="center" width="32" x="4" y="4" />
			<control changelinkcolor="no" halign="left" height="8" name="file_info_size" resize="w" type="markuptext" width="222" x="76" y="12" />
			<control changelinkcolor="no" halign="left" height="24" name="file_info_titles" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;Tamaño\nSHA256\nAnalizado&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="32" x="40" y="12" />
			<control halign="left" height="24" name="banner" resize="yw" type="markuptext" width="294" x="4" y="220" />
			<control halign="center" height="14" name="progress_bar" resize="xy" type="static" valign="center" width="20" x="4" y="248" />
			<control halign="left" height="14" name="progress_track" resize="yw" type="static" valign="top" width="190" x="4" y="248" />
			<control close="0" height="14" name="close_btn" resize="xy" title="Cancelar" type="button" width="46" x="252" y="248" />
			<control changelinkcolor="no" halign="left" height="24" name="file_info_dots" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;:\n:\n:&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="2" x="72" y="12" />
			<control changelinkcolor="no" halign="left" height="8" name="file_info_hash" resize="w" type="markuptext" width="222" x="76" y="20" />
			<control changelinkcolor="no" halign="left" height="8" name="file_info_date" resize="w" type="markuptext" width="222" x="76" y="28" />
			<control height="180" name="tabs" resize="wh" type="tab" width="294" x="4" y="38">
				<tabs>
					<tab dialog="tab_vendors" />
					<tab dialog="tab_details" />
				</tabs>
			</control>
			<control height="14" name="report_btn" resize="xy" title="Save report" type="button" visible="no" width="56" x="196" y="248" />
			<control ellipsis="path" halign="left" height="8" name="filename" resize="w" title="filename" type="static" valign="center" width="258" x="40" y="4" />
		</dialog>
	</resource>
	<resource name="tab_details" type="dialog">
		<dialog dragdrop="yes" height="250" lang="esm" resize="yes" width="298">
			<control fullrow="yes" height="130" name="list_details" noheader="yes" nosortheader="yes" resize="whs" sort="yes" type="listview" viewmode="details" width="294" x="2" y="2">
				<columns>
					<item text="Key" />
					<item text="Value" />
				</columns>
			</control>
			<control header="yes" height="64" name="group_sandbox" resize="w" title="Sandbox" type="group" width="290" x="4" y="134" />
			<control fullrow="yes" height="46" name="list_sandbox" noheader="yes" nosortheader="yes" resize="whs" sort="yes" type="listview" viewmode="details" width="294" x="2" y="144">
				<columns>
					<item text="Key" />
					<item text="Value" />
				</columns>
			</control>
			<control header="yes" height="64" name="group_sigma_analysis" resize="w" title="Sigma Analysis" type="group" width="290" x="4" y="192" />
			<control fullrow="yes" height="46" name="list_sigma_analysis" noheader="yes" nosortheader="yes" resize="whs" sort="yes" type="listview" viewmode="details" width="294" x="2" y="202">
				<columns>
					<item text="Key" />
					<item text="Value" />
				</columns>
			</control>
		</dialog>
	</resource>
	<resource name="tab_vendors" type="dialog">
		<dialog dragdrop="yes" height="250" lang="esm" resize="yes" width="298">
			<control halign="left" height="16" name="stats4" resize="ws" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;No soportado :\nNo detectado :&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="72" x="224" y="4" />
			<control fullrow="yes" height="214" name="list_vendors" resize="wh" sort="yes" type="listview" viewmode="details" width="294" x="2" y="34">
				<columns>
					<item text="Producto" />
					<item text="Versión" />
					<item text="Resultado" />
					<item text="Tipo" />
				</columns>
			</control>
			<control halign="left" height="16" name="stats3" resize="ws" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;Sospechoso :\nPeligroso :&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="72" x="150" y="4" />
			<control halign="left" height="16" name="stats2" resize="ws" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;Inofensivo :\nError :&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="72" x="76" y="4" />
			<control halign="left" height="16" name="stats1" resize="ws" title="&lt;b&gt;&lt;#%vs_listview_header_text&gt;Total :\nTimeout :&lt;/#&gt;&lt;/b&gt;" type="markuptext" width="72" x="2" y="4" />
			<control enable="no" halign="left" height="10" name="filter_vendors" resize="w" type="edit" width="132" x="14" y="22" />
			<control changelinkcolor="no" enable="no" halign="left" height="8" name="search_btn" resize="t" tips="yes" title="&lt;%ddbi:25&gt;" type="markuptext" width="10" x="4" y="24" />
			<control enable="no" height="10" name="clear_btn" resize="x" title="❌" type="button" width="12" x="146" y="22" />
		</dialog>
	</resource>
	<resource type="strings">
		<strings lang="english">
			<string id="cfg_allow_multi">Allow run multiples instances (only when NOGUI is not used).</string>
			<string id="cfg_force_fullreport">Set to True to retrieve a full &quot;file object&quot; after you get an analysis report.
This will result in a extra request per file</string>
			<string id="cfg_log_level">Logging level to be displayed. Set to OFF to show only errors.  
DEBUG to show all messages. 
STANDARD to show only the most relevant information.
WARNING to show messages that needs your attention.</string>
			<string id="cfg_max_threads">Maximum number of files to process simultaneously when using the NOGUI argument.</string>
			<string id="cfg_max_timeout">Maximum time in seconds to wait for a connection. Set to 0 for no timeout.</string>
			<string id="error_exceeded_quota">Daily quota exceeded.</string>
			<string id="error_file_upload">Error reading the response for file upload.</string>
			<string id="error_get_analysis">Error in the response for file analysis.</string>
			<string id="error_get_file_url">Error in the response when requesting a special URL for file upload.</string>
			<string id="error_get_report">Error in the response for the file report.</string>
			<string id="error_reading_hash">Error retrieving hash.</string>
			<string id="error_reading_response">Error parsing response.</string>
			<string id="error_sending_request">Error sending request.</string>
			<string id="error_uploading_file">Error uploading file.</string>
			<string id="header_file">File</string>
			<string id="header_result">Result</string>
			<string id="label_analyzed">Analyzed</string>
			<string id="label_creation_date">Creation Date</string>
			<string id="label_critical">Critical</string>
			<string id="label_first_seen_itw_date">First Seen Date</string>
			<string id="label_first_submission_date">First Submission Date</string>
			<string id="label_high">High</string>
			<string id="label_last_modification_date">Last Modification Date</string>
			<string id="label_last_submission_date">Last Submission Date</string>
			<string id="label_low">Low</string>
			<string id="label_magic">Magic</string>
			<string id="label_meaningful_name">Meaningful Name</string>
			<string id="label_medium">Medium</string>
			<string id="label_menu_json_clip">as JSON, to clipboard</string>
			<string id="label_menu_json_file">as JSON, to file</string>
			<string id="label_menu_text_clip">as Text, to clipboard</string>
			<string id="label_menu_text_file">as Text, to file</string>
			<string id="label_names">Names</string>
			<string id="label_no">No</string>
			<string id="label_report">Report</string>
			<string id="label_reputation">Reputation</string>
			<string id="label_sandbox_verdicts">Sandbox Verdicts</string>
			<string id="label_savereport">Save...</string>
			<string id="label_sigma_analysis_summary">Sigma Analysis Summary</string>
			<string id="label_signed">Signed</string>
			<string id="label_size">Size</string>
			<string id="label_stats_danger">Dangerous</string>
			<string id="label_stats_error">Error</string>
			<string id="label_stats_harmless">Harmless</string>
			<string id="label_stats_susp">Suspicious</string>
			<string id="label_stats_timeout">Timeout</string>
			<string id="label_stats_total">Total</string>
			<string id="label_stats_undet">Undetected</string>
			<string id="label_stats_unsup">Unsupported</string>
			<string id="label_summary">Summary</string>
			<string id="label_tags">Tags</string>
			<string id="label_times_submitted">Times Submitted</string>
			<string id="label_type_description">Filetype</string>
			<string id="label_vendors">Vendors</string>
			<string id="label_yes">Yes</string>
			<string id="msg_analysis_incomplete">Analysis is in the queue. Open %s to speed up this process.</string>
			<string id="msg_check_exists_VT">Checking if file already exists in VT database...</string>
			<string id="msg_checking_quota">Checking remaining quota...</string>
			<string id="msg_del_apikey">Are you sure you want to delete the stored API key? This cannot be undone.</string>
			<string id="msg_file_not_found_VT">The file does not exist in the VT database.</string>
			<string id="msg_get_hash">Getting SHA256 checksum...</string>
			<string id="msg_get_url_file">Requesting a special URL for file upload...</string>
			<string id="msg_in_use">Another dialog is already open!</string>
			<string id="msg_link_copied">Link copied to clipboard!</string>
			<string id="msg_more_info">More info</string>
			<string id="msg_no_api_key">API key is mandatory for this command to run!</string>
			<string id="msg_no_file">A file is required to continue!</string>
			<string id="msg_no_threats">No malicious/suspicious files were found.</string>
			<string id="msg_not_available">Not available</string>
			<string id="msg_set_apikey">Enter your VirusTotal API key below.</string>
			<string id="msg_string_possible">Possible %1</string>
			<string id="msg_total_time">Total time</string>
			<string id="msg_upload_file_confirm">Do you want to upload it for analysis?</string>
			<string id="msg_upload_success">Uploaded successfully!</string>
			<string id="msg_uploading_file">Uploading file...</string>
			<string id="msg_waiting_label">Waiting %s seconds to continue...</string>
			<string id="title_results_dlg">Analysis Results</string>
			<string id="title_set_apikey">Enter your VirusTotal API key</string>
			<string id="title_warning_banner">Double click in an file to open the report in your browser.
&lt;kbd&gt;Ctrl&lt;/kbd&gt; + double click to locate the file.</string>
		</strings>
		<strings lang="esm">
			<string id="cfg_allow_multi">Permitir ejecutar múltiples instancias (no se aplica a NOGUI).</string>
			<string id="cfg_force_fullreport">Establecer en True para obtener un &apos;file object&apos; completo después de recibir un informe de análisis.
Esto generará una solicitud adicional por archivo.</string>
			<string id="cfg_log_level">Nivel de registro a mostrar. OFF para mostrar solo errores. 
DEBUG para mostrar todos los mensajes. 
STANDARD para mostrar solo la información más relevante. 
WARNING para mostrar mensajes que necesitan su atención.</string>
			<string id="cfg_max_threads">Número máximo de archivos a procesar simultáneamente al usar el argumento NOGUI.</string>
			<string id="cfg_max_timeout">Tiempo máximo en segundos para esperar una conexión. Establecer en 0 para que no haya límite de tiempo.</string>
			<string id="error_exceeded_quota">Cuota diaria excedida.</string>
			<string id="error_file_upload">Error al leer la respuesta para la carga del archivo.</string>
			<string id="error_get_analysis">Error en la respuesta para el análisis del archivo.</string>
			<string id="error_get_file_url">Error en la respuesta al solicitar una URL especial para la carga del archivo.</string>
			<string id="error_get_report">Error en la respuesta para el informe del archivo.</string>
			<string id="error_reading_hash">Error al obtener el hash.</string>
			<string id="error_reading_response">Error al analizar la respuesta.</string>
			<string id="error_sending_request">Error al enviar la solicitud.</string>
			<string id="error_uploading_file">Error al subir el archivo.</string>
			<string id="header_file">Archivo</string>
			<string id="header_result">Resultado</string>
			<string id="label_analyzed">Analizado</string>
			<string id="label_creation_date">Fecha de creación</string>
			<string id="label_critical">Crítico</string>
			<string id="label_first_seen_itw_date">Fecha de primera detección</string>
			<string id="label_first_submission_date">Fecha de primer envío</string>
			<string id="label_high">Alto</string>
			<string id="label_last_modification_date">Fecha de última modificación</string>
			<string id="label_last_submission_date">Fecha de último envío</string>
			<string id="label_low">Bajo</string>
			<string id="label_magic">Magic</string>
			<string id="label_meaningful_name">Nombre significativo</string>
			<string id="label_medium">Medio</string>
			<string id="label_menu_json_clip">como JSON, al portapapeles</string>
			<string id="label_menu_json_file">como JSON, a un archivo</string>
			<string id="label_menu_text_clip">como Texto, al portapapeles</string>
			<string id="label_menu_text_file">como Texto, a un archivo</string>
			<string id="label_names">Nombres</string>
			<string id="label_no">No</string>
			<string id="label_report">Informe</string>
			<string id="label_reputation">Reputación</string>
			<string id="label_sandbox_verdicts">Veredictos del Sandbox</string>
			<string id="label_savereport">Guardar...</string>
			<string id="label_sigma_analysis_summary">Resumen del análisis Sigma</string>
			<string id="label_signed">Firmado</string>
			<string id="label_size">Tamaño</string>
			<string id="label_stats_danger">Peligroso</string>
			<string id="label_stats_error">Error</string>
			<string id="label_stats_harmless">Inofensivo</string>
			<string id="label_stats_susp">Sospechoso</string>
			<string id="label_stats_timeout">Timeout</string>
			<string id="label_stats_total">Total</string>
			<string id="label_stats_undet">No detectado</string>
			<string id="label_stats_unsup">No soportado</string>
			<string id="label_summary">Resumen</string>
			<string id="label_tags">Etiquetas</string>
			<string id="label_times_submitted">Veces enviadas</string>
			<string id="label_type_description">Tipo de archivo</string>
			<string id="label_vendors">Proveedores</string>
			<string id="label_yes">Si</string>
			<string id="msg_analysis_incomplete">El análisis está en cola. Abre %s para acelerar este proceso.</string>
			<string id="msg_check_exists_VT">Comprobando si el archivo ya existe en la base de datos de VT...</string>
			<string id="msg_checking_quota">Comprobando la cuota restante...</string>
			<string id="msg_del_apikey">¿Estás seguro de que deseas eliminar la API key almacenada? Esto no se puede deshacer.</string>
			<string id="msg_file_not_found_VT">El archivo no existe en la base de datos de VT.</string>
			<string id="msg_get_hash">Obteniendo el checksum SHA256...</string>
			<string id="msg_get_url_file">Solicitando una URL especial para la carga del archivo...</string>
			<string id="msg_in_use">¡Otro diálogo ya está abierto!</string>
			<string id="msg_link_copied">¡Enlace copiado al portapapeles!</string>
			<string id="msg_more_info">Más información</string>
			<string id="msg_no_api_key">¡La API key es obligatoria para que este comando se ejecute!</string>
			<string id="msg_no_file">¡Se requiere un archivo para continuar!</string>
			<string id="msg_no_threats">No se hallaron archivos maliciosos/sospechosos.</string>
			<string id="msg_not_available">No disponible</string>
			<string id="msg_set_apikey">Ingresa tu API key de VirusTotal a continuación.</string>
			<string id="msg_string_possible">Posiblemente %1</string>
			<string id="msg_total_time">Tiempo total</string>
			<string id="msg_upload_file_confirm">¿Deseas subirlo para su análisis?</string>
			<string id="msg_upload_success">¡Subido con éxito!</string>
			<string id="msg_uploading_file">Subiendo el archivo...</string>
			<string id="msg_waiting_label">Esperando %s segundos para continuar...</string>
			<string id="title_results_dlg">Resultados del análisis </string>
			<string id="title_set_apikey">Ingresa tu API key de VirusTotal</string>
			<string id="title_warning_banner">Doble clic en un archivo para abrir el reporte en su navegador.
&lt;kbd&gt;Ctrl&lt;/kbd&gt; + doble clic para localizarlo.</string>
		</strings>
	</resource>
	<resource name="results" type="dialog">
		<dialog height="106" lang="english" resize="yes" standard_buttons="ok" width="322">
			<control fullrow="yes" height="58" name="listview" resize="wh" type="listview" viewmode="details" width="314" x="4" y="28">
				<columns>
					<item text="File" />
					<item text="Results" />
					<item text="URL" />
				</columns>
			</control>
			<control halign="left" height="20" name="banner" resize="w" type="markuptext" width="314" x="4" y="6" />
		</dialog>
	</resource>
</resources>
