Last week, we covered a script that could take a single folder, search it, and encrypt (or redact) CCCs in project files and XML files. After thinking more about the problem, we realized that the script (while a good start) didn’t go far enough to solve the situation. First, the script only worked with EDGARLink Online XML and GFP files. Any file where the CCC appeared on a different line of the code than the tag declaring it a CCC would not have been obscured. Also, backup files created by GoFiler would not have been obscured. There are several ease of use items that should be addressed as well. The changes made to this newer version are enumerated below:
Friday, October 06. 2017
LDC #55: Protecting Your CCCs With Encryption Part 2
1) A more robust parser to look for CCC codes within XML elements.
2) Better detection function for CCCs. Instead of using a regular expression, the is_ccc function added is far more accurate.
3) The option to disable recursive searching.
4) The “Obfuscate Project” menu function. This function will close the active project file and and obscure all contents in its folder.
5) An “Obfuscation Options” menu function. This lets you change the default options for the other dialog windows.
6) An option to hook the “Obfuscate Project” function to the “Live File” function as a postprocess hook. This will automatically prompt a user to encrypt their project after a successfully submitted live filing.
7) Added the dialog resources into the main file, which makes it easier to deploy.
As you can see, these are some pretty significant changes from last week. Taken together, we believe it presents a more complete solution to keeping CCC data safe and encrypted. The new, updated script is below:
// // // GoFiler Legato Script - Obfuscate CCCs // ------------------------------------------ // // Rev 09-29-2017 // 10-03-2017 // // (c) 2017 Novaworks, LLC -- All rights reserved. // // Notes: Runs from menu hook, prompts user for a folder and obfuscation method, then obscures the CCC // in project and XML files it detects in the directory it was pointed at. Also can obscure individual // projects. // // #define ACCESSION_REGEX "\\d{10}-\\d{2}-\\d{6}" #define FIELD_WRAP "<!-- EncryptedCCC: %s -->" #define REDACTED_STRING "REDACTED;" #define MD_ENCRYPT 1 #define MD_REDACT 2 void run_obfuscate (int f_id, string mode, boolean query); /* Call from Hook Processor */ void run_options (int f_id, string mode); /* open options */ boolean obfuscate_file (string file, handle log); /* obfuscates a file. */ boolean obfuscate_ccc (string tag, int sy, handle file); /* replaces a CCC */ boolean is_obfuscatable_window (dword type); /* check if window is allowable */ boolean is_ccc (string ccc); /* checks if a string is a valid CCC */ boolean is_obfuscatable (string filetype); /* checks if the filetype is recognized */ string obfuscate_string (string input); /* obfuscates the string */ int obfuscate_project (int f_id, string mode, handle window); /* obfuscate project folder */ void post_live_obfuscate (int f_id, string mode); /* runs after live file */ int obfuscate_mode; /* current obfuscation mode */ boolean recursive; /* current recursion setting */ /****************************************/ void setup(){ /* setup - adds function to menu */ /****************************************/ int rc; /* return code */ string obscure_live; /* val from setting for post live obsc. */ string msg; /* message */ string menu[]; /* menu options array */ string desc; /* description of project */ string fn; /* script filename */ /* */ fn = GetScriptFilename(); /* gets the filename of the script */ menu["Code"] = "OBFUSCATE_OPTIONS"; /* set menu code */ menu["MenuText"] = "Obfuscation Options"; /* set menu text */ menu["Description"] = "<b>Obfuscate Options</b> "; /* set menu descriptions */ menu["Description"]+= "Options for project obfuscation."; /* set menu description */ menu["SmallBitmap"] = "OPTIONS_IMAGE_ICON"; /* set image icon */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* add function to main menu */ MenuSetHook(menu["Code"],fn,"run_options"); /* add run function to options */ } /* */ /* */ menu["Code"] = "OBFUSCATE_CCC"; /* menu code of the function */ menu["MenuText"] = "Obfuscate Folders"; /* menu description of the function */ menu["Description"]="<b>Obfuscate Folders</b> Obfuscate CCCs "; /* long description of the function */ menu["Description"]+="by either redacting or encrypting them."; /* long description of the function */ menu["SmallBitmap"] = "DIR_IMAGE_ICON"; /* image icon to use */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* add function to menu */ MenuSetHook(menu["Code"],fn,"run_obfuscate"); /* add run function to obs project */ } /* */ /* */ menu["Code"] = "OBFUSCATE_PROJECT"; /* obfuscates a project */ menu["MenuText"] = "Obfuscate Project"; /* menu text for close and encrypt proj */ desc = "<b>Encrypt Project</b> Closes and Encrypts open projects."; /* description of the function */ menu["Description"] = desc; /* description of function */ menu["SmallBitmap"] = "PROJECT_IMAGE_ICON"; /* set image icon */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* adds menu function to tools menu */ MenuSetHook(menu["Code"],fn,"obfuscate_project"); /* add run function to menu function */ } /* */ /* */ obscure_live = GetSetting("Options","Obscure After Filing"); /* get setting for obscure */ if (obscure_live == "true"){ /* if obscure after live is true */ MenuSetHook("EDGAR_SUBMIT_LIVE",fn,"post_live_obfuscate"); /* hook to file_live */ } /* */ } /* */ /****************************************/ void main(){ /* main - primary program entry pt */ /****************************************/ string windows[][]; /* open windows */ string ext; /* file extension */ int ix; /* counter */ int size; /* number of edit windows */ string window_s; /* window handle as a string val */ handle window; /* handle to open window */ /* */ if (GetScriptParent()=="LegatoIDE"){ /* if running within legato IDE */ setup(); /* setup hook on menu */ windows = EnumerateEditWindows(); /* get all edit windows */ size = ArrayGetAxisDepth(windows); /* get open window count */ for (ix = 0; ix < size; ix++){ /* iterate over all windows */ ext = GetExtension(windows[ix]["Filename"]); /* get file extension of window name */ if (ext == ".gfp"){ /* if gofiler project file */ window_s = windows[ix]["ClientHandle"]; /* get handle string */ window = MakeHandle(window_s); /* get window handle */ obfuscate_project(1,"preprocess",window); /* run the actual fucntion */ return; /* break loop */ } /* */ } /* */ MessageBox('i',"No open project files."); /* display user message */ } /* */ } /* */ /****************************************/ void post_live_obfuscate(int f_id, string mode){ /* post_live_obfuscate - runs after live*/ /****************************************/ string accession; /* accession from live filing */ string response; /* response from live file */ /* */ response = GetMenuFunctionResponse(); /* get response from live function */ if(mode=="postprocess"){ /* make sure we're running post process */ accession = GetParameter(response,"AccessionNumber"); /* get the accession num from filing */ if (IsRegexMatch(accession,ACCESSION_REGEX)){ /* if it's a valid accession num */ obfuscate_project(f_id, "preprocess"); /* obscure project */ } /* */ } /* */ } /* */ /****************************************/ void run_options(int f_id, string mode){ /* run_options - set options */ /****************************************/ int rc; /* return code */ /* */ if (mode!="preprocess"){ /* if mode is not preprocess */ return; /* exit script */ } /* */ rc = DialogBox("OptionsDlg","options_"); /* open options dialog */ } /* */ /****************************************/ void options_load(){ /* options_load - loads opts window */ /****************************************/ string mode; /* mode */ string live; /* ask after live */ string recurse; /* recurse flag */ /* */ live = GetSetting("Options","Obscure After Filing"); /* get obscure after live setting */ recurse = GetSetting("Options","Recursive"); /* get recursive flag */ mode = GetSetting("Options","Mode"); /* get mode */ /* */ if (recurse == "true"){ /* if recurse flag is true */ CheckboxSetState(OPTSRECURSIVE,BST_CHECKED); /* set state of checkbox */ } /* */ else{ /* if the flag is not true */ CheckboxSetState(OPTSRECURSIVE, BST_UNCHECKED); /* set state of checkbox */ } /* */ if (mode == "MD_ENCRYPT"){ /* if mode is encrypt */ CheckboxSetState(OPTSENCRYPT, BST_CHECKED); /* set state of checkbox */ } /* */ if (mode == "MD_REDACT"){ /* if mode is redact */ CheckboxSetState(OPTSREDACT, BST_CHECKED); /* set state of checkbox */ } /* */ if (live == "true"){ /* if obscure after live is true */ CheckboxSetState(OPTSPOSTLIVE, BST_CHECKED); /* set state of checkbox */ } /* */ else{ /* if not true */ CheckboxSetState(OPTSPOSTLIVE, BST_UNCHECKED); /* set state of checkbox */ } /* */ } /* */ /****************************************/ int options_validate(){ /* options_validate - validates opts */ /****************************************/ string msg; /* message to user */ int rc; /* user response code */ int encrypt; /* encrypt options state */ int redact; /* redact option state */ int recurse; /* recurse options state */ int live; /* state of live checkbox */ /* */ recurse = CheckboxGetState(OPTSRECURSIVE); /* get state of recursion checkbox */ encrypt = CheckboxGetState(OPTSENCRYPT); /* get state of encryption checkbox */ redact = CheckboxGetState(OPTSREDACT); /* get state of redact checkbox */ live = CheckboxGetState(OPTSPOSTLIVE); /* get state of live checkbox */ /* */ if (encrypt == redact){ /* if redact and encrypt are unchecked */ MessageBox('x',"Please select an obfuscation mode"); /* display message to user */ return ERROR_EXIT; /* exit script */ } /* */ if (redact == BST_CHECKED){ /* if redact is checked */ if (GetSetting("Options","Mode")!="MD_REDACT"){ /* if user is changing this setting */ msg = "Redacting the CCC will permanently remove it."; /* set message to user */ msg+= " Do you want to use this option?"; /* set message to user */ rc = YesNoBox('i',msg); /* make sure user wants to use redact */ if (rc!=IDYES){ /* if user didnt select yes */ return ERROR_EXIT; /* exit with error */ } /* */ } /* */ PutSetting("Options","Mode","MD_REDACT"); /* store mode setting */ } /* */ if (encrypt == BST_CHECKED){ /* if encrypt is checked */ PutSetting("Options","Mode","MD_ENCRYPT"); /* store mode setting */ } /* */ if (recurse == BST_CHECKED){ /* if recurse is checked */ PutSetting("Options","Recursive","true"); /* store recurse setting */ } /* */ else{ /* if not checked */ PutSetting("Options","Recursive","false"); /* store recurse setting */ } /* */ if (live == BST_CHECKED){ /* if live is checked */ PutSetting("Options","Obscure After Filing","true"); /* store setting for true */ setup(); /* set up hook */ } /* */ else{ /* if it's not checked */ PutSetting("Options","Obscure After Filing","false"); /* store setting for false */ } /* */ return ERROR_NONE; /* exit without error */ } /* */ /****************************************/ int obfuscate_project(int f_id, string mode, handle window){ /* obfuscate_project - obscures a GFP */ /****************************************/ int rc; /* response */ string msg; /* message to user */ string recurse_setting; /* recurse setting from settings file */ string obscure_setting; /* obscure setting from settings file */ string folderpath; /* path to the folder */ string filename; /* name of the file */ dword type; /* edit window type */ /* */ if (mode!="preprocess"){ /* if not in proeprocess */ return ERROR_NONE; /* exit script */ } /* */ if (IsWindowHandleValid(window)==false){ /* if not given a valid window handle */ window = GetActiveEditWindow(); /* get the active edit window */ } /* */ if (window == NULL_HANDLE){ /* if there is no active window */ type = 0; /* set type to zero */ } /* */ else{ /* if there is an active edit window */ type = GetEditWindowType(window); /* get the type of the window */ type &= EDX_TYPE_ID_MASK; /* mask the type */ } /* */ if (is_obfuscatable_window(type)==false){ /* if not a project */ MessageBox('i',"No open project file to encrypt."); /* display error message */ return ERROR_NONE; /* return */ } /* */ filename = GetFilename(GetEditWindowFilename(window)); /* get filename */ if (filename == ""){ /* if there is no filename */ MessageBox('x',"Save your file before obfuscating it."); /* give user error message */ return ERROR_NONE; /* return */ } /* */ obscure_setting = GetSetting("Options","Mode"); /* get setting from file */ if (obscure_setting == ""){ /* if it's not set */ rc = run_options(f_id, mode); /* run the options setup */ if (rc == ERROR_CANCEL){ /* if the user cancelled the dialog */ return rc; /* return */ } /* */ } /* */ recurse_setting = GetSetting("Options","Recursive"); /* get recurse setting from file */ if (recurse_setting == "true"){ /* if it's true */ recursive = true; /* set flag to recursive */ } /* */ else{ /* if not true */ recursive = false; /* do not recurse */ } /* */ if (obscure_setting == "MD_ENCRYPT"){ /* if mode is encrypt */ obfuscate_mode = MD_ENCRYPT; /* set to encrypt */ } /* */ else{ /* if not in encrypt mode */ obfuscate_mode = MD_REDACT; /* set to redact */ } /* */ folderpath = GetFilePath(GetEditWindowFilename(window)); /* get path to edit window file */ msg = "This will close the file %s, then obfuscate the CCCs "; /* set user message */ msg+= "of every file in the folder %s. Continue?"; /* set user message */ rc = YesNoBox('q',msg,filename, folderpath); /* */ if (rc == IDYES){ /* if user pressed yes */ PutSetting("Options","Last Folder",folderpath); /* store folder path for obscure script */ ActivateEditWindow(window); /* set closed window to active */ rc = RunMenuFunction("FILE_CLOSE"); /* close the window */ if (rc == ERROR_CANCEL){ /* if the user cancelled the operation */ return rc; /* return */ } /* */ run_obfuscate(f_id, mode, true); /* obfuscate the project file */ } /* */ return ERROR_NONE; /* return without error */ } /* */ /****************************************/ void run_obfuscate(int f_id, string mode, boolean noquery){ /* run_obfuscate - obfuscate CCC */ /****************************************/ int rc; /* return code */ handle log; /* log handle */ string msg; /* dialog message */ string target; /* target folder to obscure */ string files[]; /* array of files to obscure */ string file_path; /* path to current file */ string extension; /* extension of a file */ dword flags; /* search flags */ int ix,numfiles,modified; /* counter, num files, modified files */ /* */ if (mode!="preprocess"){ /* if not running in preprocess */ return; /* return and exit */ } /* */ /* */ if (noquery == false){ /* if not querying */ rc = DialogBox("ObfuscateDlg", "obfuscate_"); /* open the options dialog */ if (rc != ERROR_NONE){ /* if the dialog didn't exit normally */ return; /* return and exit */ } /* */ } /* */ /* */ target = GetSetting("Options","Last Folder"); /* get the target folder to obscure */ ProgressOpen("Obfuscating CCCs"); /* open a progress bar */ ProgressSetStatus("Discovering Files"); /* set the message on progress bar */ flags = FOLDER_LOAD_FOLDER_NAMES; /* set flag for searching */ if (recursive == true){ /* if we're using recursion */ flags = flags | FOLDER_LOAD_RECURSE; /* set flag for recursion */ } /* */ files = EnumerateFiles(AddPaths(target,"*.gfp;*.xml;*.bak"), /* enumerate files in target folder */ FOLDER_USE_PROGRESS | flags); /* set flag to use progress bar */ numfiles = ArrayGetAxisDepth(files); /* get the number of files in the array */ msg = "Discovered %d Possible EDGAR Files. "; /* set the dialog message */ msg+= "Obfuscate CCC Codes in Files?"; /* set the dialog message */ if (noquery == false){ /* if querying user */ rc = YesNoBox('q',msg,numfiles); /* display query to user */ if (rc!=IDYES){ /* if user did anything but press yes */ return; /* return and exit */ } /* */ } /* */ ProgressSetStatus("Obfuscating (Press 'ESC' to stop)"); /* set message on progress */ log = LogCreate("Obfuscate CCCs"); /* create a log */ for (ix=0;ix<numfiles;ix++){ /* for each file in folder */ if (ix%10 == 0){ /* on every 10th file */ ProgressSetStatus(2,"File %d of %d",ix,numfiles); /* update the progress message */ } /* */ rc = ProgressUpdate(ix,numfiles); /* update the progress bar */ if (IsError(rc)){ /* if the user cancelled it */ ProgressClose(); /* close the progress bar */ msg = "Operation stopped. %d files modified."; /* set message to user */ MessageBox('i',msg,modified); /* display message */ LogDisplay(log); /* display the log */ return; /* */ } /* */ file_path = AddPaths(target,files[ix]); /* get the path to the current file */ if (ClipFileExtension(file_path)!=""){ /* if the file has an extension */ if (obfuscate_file(file_path,log)== true){ /* if we made a change in the file */ modified++; /* increment number of modified files */ } /* */ } /* */ } /* */ ProgressClose(); /* close the progress bar */ AddMessage(log,"Modified %d Files.",modified); /* display number of files modified. */ LogDisplay(log); /* display the log */ } /* */ /****************************************/ boolean is_ccc(string ccc){ /* is_ccc - tests if a str is a ccc */ /****************************************/ char allowable[]; /* allowable characters in a CCC */ char ccc_char; /* character of the CCC */ int ix; /* counter */ boolean has_char; /* true if special char is detected */ /* */ allowable[0] = '@'; /* allow @ signs */ allowable[1] = '*'; /* allow * symbols */ allowable[2] = '#'; /* allow # symbols */ allowable[3] = '$'; /* allow $ symbol */ /* */ if (GetStringLength(ccc)!=8){ /* if less than 8 characters */ return false; /* not a CCC, return false. */ } /* */ for(ix = 0; ix < 8; ix++){ /* for each char in the CCC */ ccc_char = ccc[ix]; /* get character from array */ if (FindInString(allowable,FormatString("%c",ccc_char))>(-1)){ /* if ccc char is a special char */ if (has_char == true){ /* if we already have one */ return false; /* too many special chars, return false */ } /* */ else{ /* if we don't already have one */ has_char = true; /* set flag for special char to true */ } /* */ } /* */ else{ /* not a special character */ if (IsAlphaNumeric(ccc_char)==false){ /* if not alpha numeric */ return false; /* not a valid CCC */ } /* */ } /* */ } /* */ if (has_char == true){ /* if there's only 1 special char */ return true; /* it's a valid ccc */ } /* */ return false; /* if still in function, it's invalid. */ } /* */ /****************************************/ boolean is_obfuscatable_window(dword type){ /* is_obfuscatable_window - check window*/ /****************************************/ switch(type){ /* switch on window type */ case EDX_TYPE_EDGAR_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_13F_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_13H_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_D_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_MA_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_MFP_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_NSAR_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_S16_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_RGA_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_C_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_17_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_CFP_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_17H_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_TA_VIEW: /* if edgar view */ return true; /* return true */ } /* if none */ return false; /* return false */ } /****************************************/ boolean is_obfuscatable(string filetype){ /* is_obfuscatable - checks filetype */ /****************************************/ switch (filetype){ /* switch on the filetype */ case "FT_XML_SECTION_16": /* if section 16 */ return true; /* return true */ case "FT_XML_FORM_13H": /* if form 13h */ return true; /* return true */ case "FT_XML_FORM_C": /* if form c */ return true; /* return true */ case "FT_XML_FORM_13F": /* if 13f */ return true; /* return true */ case "FT_XML_FORM_D": /* if form d */ return true; /* return true */ case "FT_XML_FORM_MA": /* if form ma */ return true; /* return true */ case "FT_XML_FORM_N_MFP": /* if form nmfp */ return true; /* return true */ case "FT_XML_FORM_N_SAR": /* if nsar */ return true; /* return true */ case "FT_XML_EDGAR": /* if normal EDGAR XML */ return true; /* return true */ case "FT_XFDL": /* if old school XFDL */ return true; /* return true */ case "FT_GFP_3X_ELO": /* if GoFiler EDGARLinkOnline */ return true; /* return true */ case "FT_GFP_3X_13H": /* if 13H Project File */ return true; /* return true */ case "FT_GFP_3X_13F": /* if 13F Project File */ return true; /* return true */ case "FT_GFP_3X_MA": /* if MA Project File */ return true; /* return true */ case "FT_GOFILER_PROJECT_3X": /* if other GoFiler 3.x project file */ return true; /* return true */ case "FT_GOFILER_PROJECT": /* if old GoFiler Project File */ return true; /* return true */ } /* */ return false; /* return false */ } /* */ /****************************************/ boolean obfuscate_file(string path, handle log){ /* obfuscate_file - Obscures ccc in file*/ /****************************************/ handle file; /* handle to file mappdata obj */ handle wp; /* word parser handle */ string line; /* a single line of the file */ string ccc; /* the CCC in the file */ boolean modified; /* did we modify the file? */ string filetype; /* filetype of the file as string */ string msg; /* message to log */ int lines_mod; /* number of lines modified */ int ix,size,rc; /* counter, file legnth, return code */ /* */ modified = false; /* modified defaults to false */ lines_mod = 0; /* number of lines modified */ filetype = GetFileTypeString(path); /* get filetype of file */ if (is_obfuscatable(filetype)==false){ /* if not a recgnized obscurable file */ return false; /* return false (file not modified) */ } /* */ file = OpenMappedTextFile(path); /* open handle to map data object */ if (IsValidHandle(file)==false){ /* if we could not get a handle */ MessageBox('x',"Cannot open file %s",path); /* display error message */ return false; /* return false (file not modified */ } /* */ size = GetLineCount(file); /* get the length of the file */ for(ix=0;ix<size;ix++){ /* for each line in the file */ line = ReadLine(file,ix); /* read the line */ if (FindInString(line,"ccc")>0){ /* if the line contains the chars 'ccc' */ wp = WordParseCreate(WP_SGML_TAG,line); /* open handle to Wordparser on line */ ccc = WordParseGetWord(wp); /* get the first word */ while (ccc!=""){ /* while the word isn't blank */ if (FindInString(ccc,"ccc")>0 && IsSGMLTag(ccc)){ /* if CCC is in string, and it's SGML */ if(obfuscate_ccc(ccc,ix,file)){ /* replace CCC starting on line ix */ modified = true; /* set modified flag to true */ lines_mod++; /* increment lines modified */ } /* */ break; /* end loop */ } /* */ ccc = WordParseGetWord(wp); /* get the next word in the word parser */ } /* */ } /* */ } /* */ if (modified == true){ /* if we modified the file */ msg = "Obfuscated file: %s"; /* set message to user */ AddMessage(log,msg,path); /* add message to log */ msg = " CCCs Changed: %d"; /* set message to user */ AddMessage(log,msg,lines_mod); /* add message to log */ MappedTextSave(file,path); /* save the file */ } /* */ CloseHandle(file); /* close handle to file */ CloseHandle(wp); /* close the word parser */ return modified; /* return modified status of file (t/f) */ } /* */ /****************************************/ boolean obfuscate_ccc(string tag, int sy, handle file){ /* replace the SGML tag at sy in file */ /****************************************/ handle sgml; /* handle to SGML parser */ int rc; /* return code */ int x,y,ex,ey; /* x, y, end x, end y */ int spacer; /* first space char in tag */ string n_tag; /* next tag in the element */ string c_tag; /* closing SGML tag */ boolean modified; /* set default modified flag */ /* */ modified = false; /* reset modified flag */ sgml = SGMLCreate(file); /* get handle to SGML parser */ spacer = FindInString(tag," "); /* find first whitespace in tag */ if (spacer<0){ /* if there's no space */ spacer = GetStringLength(tag); /* set space to full length of tag */ } /* */ c_tag = GetStringSegment(tag,0,spacer); /* get tag up to first space */ c_tag = ReplaceInString(c_tag,"<","</"); /* make closing tag */ n_tag = "init"; /* set to non-blank val to make loop run*/ SGMLSetPosition(sgml,0,sy); /* set to start on start line */ while ((n_tag != tag) && (n_tag!="")){ /* advance to first SGML tag */ n_tag = SGMLNextElement(sgml); /* get next element */ } /* */ if (n_tag == ""){ /* if we cannot find the open tag */ CloseHandle(sgml); /* close SGML parser */ return false; /* return, no change */ } /* */ while((FindInString(n_tag,c_tag)<0) && n_tag!=""){ /* while we have a next tag */ if (is_ccc(n_tag)){ /* if it's a CCC */ x = SGMLGetItemPosSX(sgml); /* get start x pos */ y = SGMLGetItemPosSY(sgml); /* get start y pos */ ex = SGMLGetItemPosEX(sgml); /* get end x pos */ ey = SGMLGetItemPosEY(sgml); /* get end y pos */ WriteSegment(file,obfuscate_string(n_tag),x,y,ex,ey); /* write segment */ modified = true; /* set modified flag */ } /* */ n_tag = SGMLNextItem(sgml); /* advance to next SGML item */ } /* */ return modified; /* return modified value */ } /* */ /****************************************/ string obfuscate_string(string input){ /* obfuscate_string - obscure ccc */ /****************************************/ if (obfuscate_mode == MD_REDACT){ /* if we're in mark redacted mode */ return REDACTED_STRING; /* return the default redacted string. */ } /* */ else{ /* if not in redact mode */ return FormatString(FIELD_WRAP,EncryptSettingsString(input)); /* return encrypted CCC */ } /* */ } /* */ /****************************************/ void obfuscate_load(){ /* called on dialog load, populate dlg */ /****************************************/ string recurse; /* recursion setting */ string target; /* target folder to obscure */ string mode; /* mode */ /* */ mode = GetSetting("Options","Mode"); /* get mode setting */ target = GetSetting("Options","Last Folder"); /* get the last folder modified */ recurse = GetSetting("Options","Recursive"); /* get recurse setting */ if (mode=="MD_ENCRYPT"){ /* if mode is set to encrypt */ CheckboxSetState(ENCRYPT,BST_CHECKED); /* set checked state */ } /* */ else{ /* if not encrypt */ CheckboxSetState(REDACT,BST_CHECKED); /* set checked state */ } /* */ if (recurse == "true"){ /* if recurse is true */ CheckboxSetState(RECURSIVE,BST_CHECKED); /* set checked state */ } /* */ EditSetText(TARGET,target); /* set the text field on the dialog */ } /* */ /****************************************/ int obfuscate_validate(){ /* called when user presses 'ok' on dlg */ /****************************************/ string target; /* target folder to obscure */ string msg; /* message back to user */ string recurse_state; /* recurse state as string */ int recurse; /* state of recursion checkbox */ int redact, encrypt; /* radio button statuses */ int rc; /* return code */ /* */ recurse_state = "false"; /* state of recurse in file */ recursive = false; /* set recursive to false by default */ target = EditGetText(TARGET); /* get folder from dialog */ if (target == "" || IsPath(target)==false){ /* if it's not a valid path */ msg = "Please choose a valid folder location and try again."; /* set response to user */ MessageBox('x',msg); /* display error to user */ return ERROR_EXIT; /* return with error */ } /* */ redact = CheckboxGetState(REDACT); /* get status of redact radio button */ encrypt = CheckboxGetState(ENCRYPT); /* get status of encrypt radio button */ if (redact == encrypt){ /* if the buttons are the same status */ MessageBox('x',"Please choose an obfuscation mode"); /* display an error */ return ERROR_EXIT; /* return with error */ } /* */ if (redact == BST_CHECKED){ /* if redact is checked off */ msg = "CCC codes are NOT recoverable after redacting. Continue?"; /* make sure user actually means it */ rc = YesNoBox('x',msg); /* by displaying a message */ if (rc!=IDYES){ /* if they didn't press yes */ return rc; /* return their response */ } /* */ obfuscate_mode = MD_REDACT; /* store user selection */ } /* */ else{ /* if mode is not redacted */ obfuscate_mode = MD_ENCRYPT; /* store mode as encrypt */ } /* if no errors returned in validation */ recurse = CheckboxGetState(RECURSIVE); /* get state of recurse checkbox */ if (recurse == BST_CHECKED){ /* if it's checked */ recurse_state = "true"; /* set recurse state string */ recursive = true; /* set recurse global */ } /* */ PutSetting("Options","Recursive",recurse_state); /* store recurse state */ PutSetting("Options","Last Folder",target); /* store last folder obscured */ return ERROR_NONE; /* return no error */ } /* */ /****************************************/ void obfuscate_action(int control, int action){ /* called when user presses anything */ /****************************************/ string target; /* target folder to obscure */ /* */ if (control==BROWSE){ /* if user pressed browse */ target = EditGetText(TARGET); /* get the target text from dialog */ target = BrowseFolder("Select Folder to Obfuscate", target); /* open folder browse for user */ } /* */ if (target==""){ /* if the user didn't pick a valid flder*/ return; /* return */ } /* */ EditSetText(TARGET,target); /* otherwise, set selected folder val */ } /* */ #beginresource #define ENCRYPT 101 #define BROWSE_FILES 102 #define BROWSE 103 #define RECURSIVE 105 #define TARGET 106 #define REDACT 107 ObfuscateDlg DIALOG 0, 0, 215, 111 EXSTYLE WS_EX_DLGMODALFRAME STYLE DS_3DLOOK | DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU CAPTION "Obfuscate CCCs" FONT 8, "MS Sans Serif" { CONTROL "Mode", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 4, 26, 8 CONTROL "", -1, "static", SS_ETCHEDFRAME, 28, 9, 184, 1 CONTROL "Redact CCC Codes", REDACT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 18, 80, 8 CONTROL "Encrypt CCC Codes", ENCRYPT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 18, 83, 8 CONTROL "", -1, "static", SS_ETCHEDFRAME, 5, 88, 207, 1 CONTROL "Start", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 97, 93, 50, 14 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 157, 93, 50, 14 CONTROL "", TARGET, "edit", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 12, 68, 144, 12, 0 CONTROL "Browse...", BROWSE, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 158, 67, 50, 14, 0 CONTROL "Target Folder", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 52, 54, 8, 0 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 52, 57, 160, 1, 0 CONTROL "Recursive Search", RECURSIVE, "button", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 30, 96, 13, 0 } #define OPTSREDACT 201 #define OPTSENCRYPT 202 #define OPTSRECURSIVE 203 #define OPTSPOSTLIVE 204 OptionsDlg DIALOG 0, 0, 215, 91 EXSTYLE WS_EX_DLGMODALFRAME STYLE DS_3DLOOK | DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU CAPTION "Obfuscation Options" FONT 8, "MS Sans Serif" { CONTROL "Mode", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 4, 26, 8 CONTROL "", -1, "static", SS_ETCHEDFRAME, 28, 9, 184, 1 CONTROL "Redact CCC Codes", OPTSREDACT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 18, 80, 8 CONTROL "Encrypt CCC Codes", OPTSENCRYPT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 18, 83, 8 CONTROL "", -1, "static", SS_ETCHEDFRAME, 5, 66, 207, 1 CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 97, 72, 50, 14 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 157, 72, 50, 14 CONTROL "Recursive Search", OPTSRECURSIVE, "button", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 32, 96, 13, 0 CONTROL "Obscure After Live Filing", OPTSPOSTLIVE, "button", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 48, 96, 13, 0 } DIR_IMAGE_ICON BITMAP { '42 4D E8 04 00 00 00 00 00 00 36 00 00 00 28 00' '00 00 14 00 00 00 14 00 00 00 01 00 18 00 00 00' '00 00 B2 04 00 00 12 0B 00 00 12 0B 00 00 00 00' '00 00 00 00 00 00 CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF E3 C5' 'A8 B8 82 4D B8 81 4C B7 80 4A E3 C5 A8 CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF DC BE A1 B6 7E 48' 'B8 81 4C B6 7E 48 E3 C5 A8 CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF D5 B7 9A B6 7E 48 B6 7E 48 B6' '7E 48 E3 C5 A8 CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF DC C2 A8 B6 7E 48 B6 7E 48 B6 7E 48 E3 C5' 'A8 CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF 82 C2 EA 82 C2 EA 82 C2' 'EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 77 8E E1' '38 5A D6 77 8E E1 82 C2 EA 82 C2 EA 82 C2 EA 82' 'C2 EA CF E7 F6 CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA' '82 C2 EA 82 C2 EA 82 C2 EA 77 8E E1 38 5A D6 77' '8E E1 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA AD D7' 'F0 CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' '82 C2 EA 9E D0 EF 82 C2 EA 82 C2 EA 82 C2 EA 82' 'C2 EA 82 C2 EA 82 C2 EA 77 8E E1 82 C2 EA 82 C2' 'EA 82 C2 EA 82 C2 EA 82 C2 EA 8E C7 EB CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF 82 C2 EA D3' 'E9 F8 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2' 'EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA' '82 C2 EA 82 C2 EA 82 C2 EA CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF 82 C2 EA 38 5A D6 8E C8' 'EC 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA' '82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82' 'C2 EA 82 C2 EA CB E5 F5 CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF 82 C2 EA 38 5A D6 C1 E1 F5 82 C2 EA' '82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82' 'C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2' 'EA A9 D4 F0 CC 00 FF CC 00 FF CC 00 FF CC 00 FF' '82 C2 EA 38 5A D6 38 5A D6 85 C3 EA 82 C2 EA 82' 'C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2' 'EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 8B C6 EB' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF 82 C2 EA 38' '5A D6 38 5A D6 CD E7 F7 85 C3 EA 82 C2 EA 82 C2' 'EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA 82 C2 EA' '82 C2 EA 82 C2 EA 82 C2 EA A6 D3 EF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF 82 C2 EA 38 5A D6 38 5A' 'D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6' '38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 82' 'C2 EA CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF 82 C2 EA D6 EB F8 38 5A D6 38 5A D6' '38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38' '5A D6 38 5A D6 38 5A D6 38 5A D6 82 C2 EA CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF 84 C2 E9 82 C2 EA 82 C2 EA 82 C2 EA 82' 'C2 EA 82 C2 EA 82 C2 EA A7 D4 EF FE FE FF FF FF' 'FF FF FF FF FF FF FF 82 C2 EA CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF A1 D1 EF B5 DA F1 FF FF FF FF FF FF' 'FF FF FF 82 C2 EA CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF 98 CC ED 82 C2 EA 82 C2 EA 82 C2 EA B0' 'D8 F1 CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF 00 00 ' } OPTIONS_IMAGE_ICON BITMAP { '42 4D E8 04 00 00 00 00 00 00 36 00 00 00 28 00' '00 00 14 00 00 00 14 00 00 00 01 00 18 00 00 00' '00 00 B2 04 00 00 12 0B 00 00 12 0B 00 00 00 00' '00 00 00 00 00 00 CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF 38 5A D6 64 7E DF A5 B4 EC CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF 64 7E DF 38 5A D6 38 5A D6 38 5A D6 CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF A5 B4 EC' '38 5A D6 38 5A D6 CC 00 FF BE 8C 5C CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF 38 5A D6 CC' '00 FF B8 82 4D B8 82 4D BE 8C 5C CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF BE 8C 5C B8 82' '4D B8 82 4D B8 82 4D BE 8C 5C CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF BF 8E 5E B8 82 4D' 'B8 82 4D B8 82 4D BE 8C 5C CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF A5 B4 EC 64 7E DF' '36 58 D6 36 58 D6 CC 00 FF BF 8E 5E B8 82 4D B8' '82 4D B8 82 4D BE 8C 5C CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF 55 72 DC 32 55 D6 32 55 D6 32 55 D6 32' '55 D6 32 55 D6 CC 00 FF BF 8E 5E B8 82 4D B8 82' '4D B8 82 4D BE 8C 5C CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF 55 72 DC 32' '55 D6 32 55 D6 3E 5F D7 93 A5 DD 93 A5 DD 32 55' 'D6 32 55 D6 CC 00 FF BF 8E 5E B8 82 4D B8 82 4D' 'B8 82 4D BE 8C 5C CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF A5 B4 EC 32 55 D6 32 55 D6 32 55' 'D6 87 9B DC F3 F5 E3 F3 F5 E3 93 A5 DD 32 55 D6' '32 55 D6 CC 00 FF BF 8E 5E B8 82 4D B8 82 4D B8' '82 4D BE 8C 5C CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF 5F 7A DE 32 55 D6 32 55 D6 32 55 D6 3E 5F D7' '93 A5 DD 93 A5 DD 32 55 D6 32 55 D6 32 55 D6 32' '55 D6 CC 00 FF BF 8E 5E B8 82 4D B8 82 4D B8 82' '4D BE 8C 5C CC 00 FF CC 00 FF CC 00 FF 36 58 D6' '32 55 D6 32 55 D6 32 55 D6 3E 5F D7 F3 F5 E3 F3' 'F5 E3 32 55 D6 32 55 D6 32 55 D6 32 55 D6 36 58' 'D6 CC 00 FF BF 8E 5E B8 82 4D B8 82 4D B8 82 4D' 'BE 8C 5C CC 00 FF CC 00 FF 36 58 D6 32 55 D6 32' '55 D6 32 55 D6 62 7D D9 F3 F5 E3 F3 F5 E3 62 7D' 'D9 32 55 D6 32 55 D6 32 55 D6 36 58 D6 CC 00 FF' 'CC 00 FF BF 8E 5E B8 82 4D B8 82 4D B8 82 4D BE' '8C 5C CC 00 FF 61 7C DF 32 55 D6 32 55 D6 32 55' 'D6 93 A5 DD F3 F5 E3 F3 F5 E3 93 A5 DD 32 55 D6' '32 55 D6 32 55 D6 61 7C DF CC 00 FF CC 00 FF CC' '00 FF BF 8E 5E B8 82 4D B8 82 4D CC 00 FF CC 00' 'FF A7 B6 ED 32 55 D6 32 55 D6 32 55 D6 93 A5 DD' 'F3 F5 E3 F3 F5 E3 93 A5 DD 32 55 D6 32 55 D6 32' '55 D6 A7 B6 ED CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF C0 8F 60 CC 00 FF CC 00 FF CC 00 FF CC 00 FF' '55 72 DC 32 55 D6 32 55 D6 93 A5 DD F3 F5 E3 F3' 'F5 E3 93 A5 DD 32 55 D6 32 55 D6 55 72 DC CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF 58' '74 DD 32 55 D6 32 55 D6 93 A5 DD 93 A5 DD 32 55' 'D6 32 55 D6 58 74 DD CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF A7 B6' 'ED 64 7E DF 36 58 D6 36 58 D6 64 7E DF A7 B6 ED' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF 00 00 ' } PROJECT_IMAGE_ICON BITMAP { '42 4D E8 04 00 00 00 00 00 00 36 00 00 00 28 00' '00 00 14 00 00 00 14 00 00 00 01 00 18 00 00 00' '00 00 B2 04 00 00 12 0B 00 00 12 0B 00 00 00 00' '00 00 00 00 00 00 CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF E3 C5' 'A8 B8 82 4D B8 81 4C B7 80 4A E3 C5 A8 CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF DC BE A1 B6 7E 48' 'B8 81 4C B6 7E 48 E3 C5 A8 CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF D5 B7 9A B6 7E 48 B6 7E 48 B6' '7E 48 E3 C5 A8 CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF DC C2 A8 B6 7E 48 B6 7E 48 B6 7E 48 E3 C5' 'A8 CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF 77 8E E1' '38 5A D6 77 8E E1 CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF 77 8E E1 38 5A D6 77' '8E E1 CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' '72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72' '72 72 CC 00 FF CC 00 FF 77 8E E1 CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF 72 72 72 72' '72 72 72 72 72 72 72 72 72 72 72 72 72 72 CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1' '77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77' '8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E' 'E1 77 8E E1 77 8E E1 CC 00 FF CC 00 FF 77 8E E1' '38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38' '5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A' 'D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6' '77 8E E1 CC 00 FF CC 00 FF 77 8E E1 38 5A D6 38' '5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A' 'D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6' '38 5A D6 38 5A D6 38 5A D6 38 5A D6 77 8E E1 CC' '00 FF CC 00 FF 77 8E E1 77 8E E1 77 8E E1 77 8E' 'E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1' '77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77' '8E E1 77 8E E1 77 8E E1 77 8E E1 CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF 77 8E E1 77 8E E1 77' '8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E' 'E1 77 8E E1 77 8E E1 CC 00 FF CC 00 FF CC 00 FF' '72 72 72 72 72 72 72 72 72 72 72 72 72 72 72 72' '72 72 CC 00 FF 77 8E E1 38 5A D6 38 5A D6 38 5A' 'D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6 38 5A D6' '77 8E E1 CC 00 FF CC 00 FF CC 00 FF 72 72 72 72' '72 72 72 72 72 72 72 72 72 72 72 72 72 72 CC 00' 'FF 77 8E E1 38 5A D6 38 5A D6 38 5A D6 38 5A D6' '38 5A D6 38 5A D6 38 5A D6 38 5A D6 77 8E E1 CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF 77 8E E1' '77 8E E1 77 8E E1 77 8E E1 77 8E E1 77 8E E1 77' '8E E1 77 8E E1 77 8E E1 77 8E E1 CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC' '00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00' 'FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF CC 00 FF' 'CC 00 FF CC 00 FF 00 00 ' } #endresource
The CCC_REGEX definition has been removed, and the ACCESSION_REGEX has been added. This matches only valid accession numbers back from the SEC. The run_obfuscate function has a new parameter, query, which if true will run the function without prompting for user input. The run_options function is a new function that launches an options configuration dialog. Also, a new obfuscate_ccc function was added to handle parsing a single CCC tag in an XML file, instead of the somewhat hokey way it was done in the previous version of the script. It’s now called as part of the obfuscate_file function.
The new function is_obfuscatable_window was added. It is basically the same type of function as is_obfuscatable from last week, but it takes a window type instead of a file type. The is_ccc function was added to check if a string is a CCC instead of just using a regular expression. The obfuscate_project function has been added to take care of obfuscating a single project instead of an entire directory. And, finally, the post_live_obfuscate function was added. It’s called as a post process operation for live filings. If the user enables it, this function will run the ofuscate_project function if applicable.
#define ACCESSION_REGEX "\\d{10}-\\d{2}-\\d{6}" #define FIELD_WRAP "<!-- EncryptedCCC: %s -->" #define REDACTED_STRING "REDACTED;" #define MD_ENCRYPT 1 #define MD_REDACT 2 void run_obfuscate (int f_id, string mode,boolean noquery);/* Call from Hook Processor */ void run_options (int f_id, string mode); /* open options */ boolean obfuscate_file (string file, handle log); /* obfuscates a file. */ boolean obfuscate_ccc (string tag, int sy, handle file); /* replaces a CCC */ boolean is_obfuscatable_window (dword type); /* check if window is allowable */ boolean is_ccc (string ccc); /* checks if a string is a valid CCC */ boolean is_obfuscatable (string filetype); /* checks if the filetype is recognized */ string obfuscate_string (string input); /* obfuscates the string */ int obfuscate_project (int f_id, string mode, handle window); /* obfuscate project folder */ void post_live_obfuscate (int f_id, string mode); /* runs after live file */ int obfuscate_mode; /* current obfuscation mode */ boolean recursive; /* current recursion setting */
Our setup function this week is quite a bit longer, but it’s still pretty simple. We’ve added two additional menu functions, “OBFUSCATE_OPTIONS” and “OBFUSCATE_PROJECT”. The setup function is going to be called more frequently this time as well, so instead of just running MenuAddFunction each time it’s called, we use the MenuFindFunctionID function to check if the menu item already exists before trying to add it. We also need to check the setting “Obscure After Filing” to see if it’s true, because if so, we must hook our post_live_obfuscate function to the menu function “EDGAR_SUBMIT_LIVE”.
/****************************************/ void setup(){ /* setup - adds function to menu */ /****************************************/ int rc; /* return code */ string obscure_live; /* val from setting for post live obsc. */ string msg; /* message */ string menu[]; /* menu options array */ string desc; /* description of project */ string fn; /* script filename */ /* */ fn = GetScriptFilename(); /* gets the filename of the script */ menu["Code"] = "OBFUSCATE_OPTIONS"; /* set menu code */ menu["MenuText"] = "Obfuscation Options"; /* set menu text */ menu["Description"] = "<b>Obfuscate Options</b> "; /* set menu descriptions */ menu["Description"]+= "Options for project obfuscation."; /* set menu description */ menu["SmallBitmap"] = "OPTIONS_IMAGE_ICON"; /* set image icon */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* add function to main menu */ MenuSetHook(menu["Code"],fn,"run_options"); /* add run function to options */ } /* */ /* */ menu["Code"] = "OBFUSCATE_CCC"; /* menu code of the function */ menu["MenuText"] = "Obfuscate Folders"; /* menu description of the function */ menu["Description"]="<b>Obfuscate Folders</b> Obfuscate CCCs "; /* long description of the function */ menu["Description"]+="by either redacting or encrypting them."; /* long description of the function */ menu["SmallBitmap"] = "DIR_IMAGE_ICON"; /* image icon to use */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* add function to menu */ MenuSetHook(menu["Code"],fn,"run_obfuscate"); /* add run function to obs project */ } /* */ /* */ menu["Code"] = "OBFUSCATE_PROJECT"; /* obfuscates a project */ menu["MenuText"] = "Obfuscate Project"; /* menu text for close and encrypt proj */ desc = "<b>Encrypt Project</b> Closes and Encrypts open projects."; /* description of the function */ menu["Description"] = desc; /* description of function */ menu["SmallBitmap"] = "PROJECT_IMAGE_ICON"; /* set image icon */ if (MenuFindFunctionID(menu["Code"])==(-1)){ /* if it's not already on the menu */ MenuAddFunction(menu); /* adds menu function to tools menu */ MenuSetHook(menu["Code"],fn,"obfuscate_project"); /* add run function to menu function */ } /* */ /* */ obscure_live = GetSetting("Options","Obscure After Filing"); /* get setting for obscure */ if (obscure_live == "true"){ /* if obscure after live is true */ MenuSetHook("EDGAR_SUBMIT_LIVE",fn,"post_live_obfuscate"); /* hook to file_live */ } /* */ } /* */
The main function is quite a bit fancier now as well. It’s much easier to run and debug functions through the IDE instead of trying to figure out why your function isn’t working through examining log files, so the main function now attempts to run the obfuscate_project function on any open project files. It does this by running the EnumerateEditWindows function to get a list of all open edit windows, iterating over all of them, and checking the extension of the open file. If it’s a GFP file, we get the handle to the window with the MakeHandle SDK function and pass it to obfuscate_project so it can attempt to run the function on a project file. Changes like this can make it a lot easier to debug the functions as the script is being developed. Even though the end user will likely never run the main function, it can save the developer time.
/****************************************/ void main(){ /* main - primary program entry pt */ /****************************************/ string windows[][]; /* open windows */ string ext; /* file extension */ int ix; /* counter */ int size; /* number of edit windows */ string window_s; /* window handle as a string val */ handle window; /* handle to open window */ /* */ if (GetScriptParent()=="LegatoIDE"){ /* if running within legato IDE */ setup(); /* setup hook on menu */ windows = EnumerateEditWindows(); /* get all edit windows */ size = ArrayGetAxisDepth(windows); /* get open window count */ for (ix = 0; ix < size; ix++){ /* iterate over all windows */ ext = GetExtension(windows[ix]["Filename"]); /* get file extension of window name */ if (ext == ".gfp"){ /* if gofiler project file */ window_s = windows[ix]["ClientHandle"]; /* get handle string */ window = MakeHandle(window_s); /* get window handle */ obfuscate_project(1,"preprocess",window); /* run the actual fucntion */ return; /* break loop */ } /* */ } /* */ MessageBox('i',"No open project files."); /* display user message */ } /* */ } /* */
This function, post_live_obfuscate, is run after every live filing if it’s hooked onto the live filing process. The first thing it does is run the GetMenuFunctionResponse function to get the response from the live file function. If it’s running in postprocess mode, it means the filing function has finished running, so we can examine the response to see what happened. Using the GetParameter function, we can try to get the Accession Number out of the response. We can use the IsRegexMatch function to compare the result with our ACCESSION_REGEX regular expression. If it matches, it means the live filing was successfully transmitted and we have an accession number, so our script can run our obfuscate_project function. The idea behind this is that after you’ve live filed a project, it’s most likely done, so you can obfuscate it and move on right away.
/****************************************/ void post_live_obfuscate(int f_id, string mode){ /* post_live_obfuscate - runs after live*/ /****************************************/ string accession; /* accession from live filing */ string response; /* response from live file */ /* */ response = GetMenuFunctionResponse(); /* get response from live function */ if(mode=="postprocess"){ /* make sure we're running post process */ accession = GetParameter(response,"AccessionNumber"); /* get the accession num from filing */ if (IsRegexMatch(accession,ACCESSION_REGEX)){ /* if it's a valid accession num */ obfuscate_project(f_id, "preprocess"); /* obscure project */ } /* */ } /* */ } /* */
The run_options function is extremely simple. It checks the current running mode to make sure it’s preprocess, and if so, opens the dialog box to edit the options.
/****************************************/ void run_options(int f_id, string mode){ /* run_options - set options */ /****************************************/ int rc; /* return code */ /* */ if (mode!="preprocess"){ /* if mode is not preprocess */ return; /* exit script */ } /* */ rc = DialogBox("OptionsDlg","options_"); /* open options dialog */ } /* */ /****************************************/
The options_load function reads the settings file to get the live, recurse, and mode settings. It then checks or unchecks the appropriate checkboxes based on the loaded values.
/****************************************/ void options_load(){ /* options_load - loads opts window */ /****************************************/ string mode; /* mode */ string live; /* ask after live */ string recurse; /* recurse flag */ /* */ live = GetSetting("Options","Obscure After Filing"); /* get obscure after live setting */ recurse = GetSetting("Options","Recursive"); /* get recursive flag */ mode = GetSetting("Options","Mode"); /* get mode */ /* */ if (recurse == "true"){ /* if recurse flag is true */ CheckboxSetState(OPTSRECURSIVE,BST_CHECKED); /* set state of checkbox */ } /* */ else{ /* if the flag is not true */ CheckboxSetState(OPTSRECURSIVE, BST_UNCHECKED); /* set state of checkbox */ } /* */ if (mode == "MD_ENCRYPT"){ /* if mode is encrypt */ CheckboxSetState(OPTSENCRYPT, BST_CHECKED); /* set state of checkbox */ } /* */ if (mode == "MD_REDACT"){ /* if mode is redact */ CheckboxSetState(OPTSREDACT, BST_CHECKED); /* set state of checkbox */ } /* */ if (live == "true"){ /* if obscure after live is true */ CheckboxSetState(OPTSPOSTLIVE, BST_CHECKED); /* set state of checkbox */ } /* */ else{ /* if not true */ CheckboxSetState(OPTSPOSTLIVE, BST_UNCHECKED); /* set state of checkbox */ } /* */ } /* */
The options_validate function gets the recurse, encrypt, redact, and live values from the dialog with the CheckboxGetState SDK function. If the encrypt and redact values are the same, it means neither was chosen, so we need to display an error and also return an error. If redact is checked, and redact wasn’t already checked, we need to display a warning message to the user to let them know redacting a CCC is permanent and confirm they really want to do this. If the users presses anything but “Yes” on the dialog, we can simply return an error.
After we have our values, we can just use the PutSetting function to store the selected values of the options chosen.
/****************************************/ int options_validate(){ /* options_validate - validates opts */ /****************************************/ string msg; /* message to user */ int rc; /* user response code */ int encrypt; /* encrypt options state */ int redact; /* redact option state */ int recurse; /* recurse options state */ int live; /* state of live checkbox */ /* */ recurse = CheckboxGetState(OPTSRECURSIVE); /* get state of recursion checkbox */ encrypt = CheckboxGetState(OPTSENCRYPT); /* get state of encryption checkbox */ redact = CheckboxGetState(OPTSREDACT); /* get state of redact checkbox */ live = CheckboxGetState(OPTSPOSTLIVE); /* get state of live checkbox */ /* */ if (encrypt == redact){ /* if redact and encrypt are unchecked */ MessageBox('x',"Please select an obfuscation mode"); /* display message to user */ return ERROR_EXIT; /* exit script */ } /* */ if (redact == BST_CHECKED){ /* if redact is checked */ if (GetSetting("Options","Mode")!="MD_REDACT"){ /* if user is changing this setting */ msg = "Redacting the CCC will permanently remove it."; /* set message to user */ msg+= " Do you want to use this option?"; /* set message to user */ rc = YesNoBox('i',msg); /* make sure user wants to use redact */ if (rc!=IDYES){ /* if user didnt select yes */ return ERROR_EXIT; /* exit with error */ } /* */ } /* */ PutSetting("Options","Mode","MD_REDACT"); /* store mode setting */ } /* */ if (encrypt == BST_CHECKED){ /* if encrypt is checked */ PutSetting("Options","Mode","MD_ENCRYPT"); /* store mode setting */ } /* */ if (recurse == BST_CHECKED){ /* if recurse is checked */ PutSetting("Options","Recursive","true"); /* store recurse setting */ } /* */ else{ /* if not checked */ PutSetting("Options","Recursive","false"); /* store recurse setting */ } /* */ if (live == BST_CHECKED){ /* if live is checked */ PutSetting("Options","Obscure After Filing","true"); /* store setting for true */ setup(); /* set up hook */ } /* */ else{ /* if it's not checked */ PutSetting("Options","Obscure After Filing","false"); /* store setting for false */ } /* */ return ERROR_NONE; /* exit without error */ } /* */
The obfuscate_project function acts as an extension of the run_obfuscate function. It can take a window handle as an input, but if no window is given, it assumes the currently open window is the target. If the returned window is NULL_HANDLE, it returns and exits the script. It tests the window handle with the is_obfuscatable_window function, and if its an invalid window type, displays an error message and returns. If it’s a valid window, we can test the name of the file to make sure it’s not blank (a blank name means an unsaved file), then we can retrieve our settings using the GetSetting function. If we can’t read a setting for mode, we can tell that this function has never been run. So we can call run_options to force the user to set up the options. If they cancel the dialog, we can just return right here. Otherwise, we can get the rest of our settings and set up our variables.
/****************************************/ int obfuscate_project(int f_id, string mode, handle window){ /* obfuscate_project - obscures a GFP */ /****************************************/ int rc; /* response */ string msg; /* message to user */ string recurse_setting; /* recurse setting from settings file */ string obscure_setting; /* obscure setting from settings file */ string folderpath; /* path to the folder */ string filename; /* name of the file */ dword type; /* edit window type */ /* */ if (mode!="preprocess"){ /* if not in proeprocess */ return ERROR_NONE; /* exit script */ } /* */ if (IsWindowHandleValid(window)==false){ /* if not given a valid window handle */ window = GetActiveEditWindow(); /* get the active edit window */ } /* */ if (window == NULL_HANDLE){ /* if there is no active window */ type = 0; /* set type to zero */ } /* */ else{ /* if there is an active edit window */ type = GetEditWindowType(window); /* get the type of the window */ type &= EDX_TYPE_ID_MASK; /* mask the type */ } /* */ if (is_obfuscatable_window(type)==false){ /* if not a project */ MessageBox('i',"No open project file to encrypt."); /* display error message */ return ERROR_NONE; /* return */ } /* */ filename = GetFilename(GetEditWindowFilename(window)); /* get filename */ if (filename == ""){ /* if there is no filename */ MessageBox('x',"Save your file before obfuscating it."); /* give user error message */ return ERROR_NONE; /* return */ } /* */ obscure_setting = GetSetting("Options","Mode"); /* get setting from file */ if (obscure_setting == ""){ /* if it's not set */ rc = run_options(f_id, mode); /* run the options setup */ if (rc == ERROR_CANCEL){ /* if the user cancelled the dialog */ return rc; /* return */ } /* */ } /* */ recurse_setting = GetSetting("Options","Recursive"); /* get recurse setting from file */ if (recurse_setting == "true"){ /* if it's true */ recursive = true; /* set flag to recursive */ } /* */ else{ /* if not true */ recursive = false; /* do not recurse */ } /* */ if (obscure_setting == "MD_ENCRYPT"){ /* if mode is encrypt */ obfuscate_mode = MD_ENCRYPT; /* set to encrypt */ } /* */ else{ /* if not in encrypt mode */ obfuscate_mode = MD_REDACT; /* set to redact */ } /* */
Using the GetFilePath function in conjunction with the GetEditWindowFilename function, we can grab the path to the folder and display a warning message to the user. If they select “Yes” on the dialog to continue, we can set the option for the folder path into our settings file (our run_obfuscate function already looks there to find the path), use the ActivateEditWindow function to ensure our desired window is active, use the RunMenuFunction function to trigger a FILE_CLOSE operation, then actually call run_obfuscate. We’re calling it with the noquery parameter set to true, so it won’t ask anything. It will just pick up the settings this function sets up.
folderpath = GetFilePath(GetEditWindowFilename(window)); /* get path to edit window file */ msg = "This will close the file %s, then obfuscate the CCCs "; /* set user message */ msg+= "of every file in the folder %s. Continue?"; /* set user message */ rc = YesNoBox('q',msg,filename, folderpath); /* */ if (rc == IDYES){ /* if user pressed yes */ PutSetting("Options","Last Folder",folderpath); /* store folder path for obscure script */ ActivateEditWindow(window); /* set closed window to active */ RunMenuFunction("FILE_CLOSE"); /* close the window */ run_obfuscate(f_id, mode, true); /* obfuscate the project file */ } /* */ return ERROR_NONE; /* return without error */ } /* */
The run_obfuscate function has changed slightly. The DialogBox function is wrapped by an if statement that checks to see if noquery is enabled. If so, it doesn’t prompt the user for input and therefore doesn’t open the dialog. Another change is that before running the EnumerateFiles function, the run_obfuscate function checks to see if recursion is enabled. If so, it sets the recursion flag. Otherwise, the script runs in non-recursive mode. It also checks once more to see if noquery is enabled before prompting the user to continue. If in noquery mode, it doesn’t bother asking, it just runs.
/****************************************/ void run_obfuscate(int f_id, string mode, boolean noquery){ /* run_obfuscate - obfuscate CCC */ /****************************************/ int rc; /* return code */ handle log; /* log handle */ string msg; /* dialog message */ string target; /* target folder to obscure */ string files[]; /* array of files to obscure */ string file_path; /* path to current file */ string extension; /* extension of a file */ dword flags; /* search flags */ int ix,numfiles,modified; /* counter, num files, modified files */ /* */ if (mode!="preprocess"){ /* if not running in preprocess */ return; /* return and exit */ } /* */ /* */ if (noquery == false){ /* if not querying */ rc = DialogBox("ObfuscateDlg", "obfuscate_"); /* open the options dialog */ if (rc != ERROR_NONE){ /* if the dialog didn't exit normally */ return; /* return and exit */ } /* */ } /* */ /* */ target = GetSetting("Options","Last Folder"); /* get the target folder to obscure */ ProgressOpen("Obfuscating CCCs"); /* open a progress bar */ ProgressSetStatus("Discovering Files"); /* set the message on progress bar */ flags = FOLDER_LOAD_FOLDER_NAMES; /* set flag for searching */ if (recursive == true){ /* if we're using recursion */ flags = flags | FOLDER_LOAD_RECURSE; /* set flag for recursion */ } /* */ files = EnumerateFiles(AddPaths(target,"*.gfp;*.xml;*.bak"), /* enumerate files in target folder */ FOLDER_USE_PROGRESS | flags); /* set flag to use progress bar */ numfiles = ArrayGetAxisDepth(files); /* get the number of files in the array */ msg = "Discovered %d Possible EDGAR Files. "; /* set the dialog message */ msg+= "Obfuscate CCC Codes in Files?"; /* set the dialog message */ if (noquery == false){ /* if querying user */ rc = YesNoBox('q',msg,numfiles); /* display query to user */ if (rc!=IDYES){ /* if user did anything but press yes */ return; /* return and exit */ } /* */ } /* */ ProgressSetStatus("Obfuscating (Press 'ESC' to stop)"); /* set message on progress */ log = LogCreate("Obfuscate CCCs"); /* create a log */ for (ix=0;ix<numfiles;ix++){ /* for each file in folder */ if (ix%10 == 0){ /* on every 10th file */ ProgressSetStatus(2,"File %d of %d",ix,numfiles); /* update the progress message */ } /* */ rc = ProgressUpdate(ix,numfiles); /* update the progress bar */ if (IsError(rc)){ /* if the user cancelled it */ ProgressClose(); /* close the progress bar */ msg = "Operation stopped. %d files modified."; /* set message to user */ MessageBox('i',msg,modified); /* display message */ LogDisplay(log); /* display the log */ return; /* */ } /* */ file_path = AddPaths(target,files[ix]); /* get the path to the current file */ if (ClipFileExtension(file_path)!=""){ /* if the file has an extension */ if (obfuscate_file(file_path,log)== true){ /* if we made a change in the file */ modified++; /* increment number of modified files */ } /* */ } /* */ } /* */ ProgressClose(); /* close the progress bar */ AddMessage(log,"Modified %d Files.",modified); /* display number of files modified. */ LogDisplay(log); /* display the log */ } /* */
The is_ccc function takes a string and returns true or false, depending on if that string is a valid CCC code or not. It defines a character array called allowable, which contains allowed characters in a CCC. Then it checks the length the input. If it’s not 8 characters exactly, it returns false because the CCC must be 8 characters. Next, it loops through all characters in the input string. For each character, we use the FindInString function to see if our character is in our character array. Legato treats character arrays as through they are strings, so we can use our allowable array as the string parameter for the FindInString function. If the character is a special character and if the has_char flag is true, it means we’ve already had a special character, and we can return false, because a CCC must only have one special character. Otherwise, we can set has_char to true and keep going, because that means it’s our first special character. If the current character isn’t a special character, we can use the IsAlphaNumeric function to test if it’s an alpha numeric value. If it’s not, we can return false, because it’s an invalid character for a CCC code. Otherwise, we can keep on parsing. Once we’ve gotten through all 8 characters without returning, we just need to check if has_char is true. If so, it’s a valid CCC with a single special character. If not, we can return false, because it doesn’t have a special character.
/****************************************/ boolean is_ccc(string ccc){ /* is_ccc - tests if a str is a ccc */ /****************************************/ char allowable[]; /* allowable characters in a CCC */ char ccc_char; /* character of the CCC */ int ix; /* counter */ boolean has_char; /* true if special char is detected */ /* */ allowable[0] = '@'; /* allow @ signs */ allowable[1] = '*'; /* allow * symbols */ allowable[2] = '#'; /* allow # symbols */ allowable[3] = '$'; /* allow $ symbol */ /* */ if (GetStringLength(ccc)!=8){ /* if less than 8 characters */ return false; /* not a CCC, return false. */ } /* */ for(ix = 0; ix < 8; ix++){ /* for each char in the CCC */ ccc_char = ccc[ix]; /* get character from array */ if (FindInString(allowable,FormatString("%c",ccc_char))>(-1)){ /* if ccc char is a special char */ if (has_char == true){ /* if we already have one */ return false; /* too many special chars, return false */ } /* */ else{ /* if we don't already have one */ has_char = true; /* set flag for special char to true */ } /* */ } /* */ else{ /* not a special character */ if (IsAlphaNumeric(ccc_char)==false){ /* if not alpha numeric */ return false; /* not a valid CCC */ } /* */ } /* */ } /* */ if (has_char == true){ /* if there's only 1 special char */ return true; /* it's a valid ccc */ } /* */ return false; /* if still in function, it's invalid. */ } /* */
The next function, is_obfuscatable_window, is very simple. It takes a window type as an input, and using a large switch statement, returns true if that window on the list of window types that should be obfuscated. Otherwise, it returns false.
/****************************************/ boolean is_obfuscatable_window(dword type){ /* is_obfuscatable_window - check window*/ /****************************************/ switch(type){ /* switch on window type */ case EDX_TYPE_EDGAR_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_13F_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_13H_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_D_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_MA_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_MFP_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_NSAR_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_S16_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_RGA_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_C_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_17_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_CFP_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_17H_VIEW: /* if edgar view */ return true; /* return true */ case EDX_TYPE_XML_TA_VIEW: /* if edgar view */ return true; /* return true */ } /* if none */ return false; /* return false */ }
The obfuscate_file function works the same way as before, but we changed the way it replaces CCC codes to a new function, obfuscate_ccc. If it finds an SGML tag that contains the characters ‘ccc’, it assumes it’s a CCC XML tag and passes the tag it found, the line number it’s on, and the handle to the Mapped Data Object to our obfuscate_ccc function, which handles it from there. If the obfuscate_ccc function replaces something, it returns true, so we can mark our file as modified and increment the number of lines modified.
/****************************************/ boolean obfuscate_file(string path, handle log){ /* obfuscate_file - Obscures ccc in file*/ /****************************************/ handle file; /* handle to file mappdata obj */ handle wp; /* word parser handle */ string line; /* a single line of the file */ string ccc; /* the CCC in the file */ boolean modified; /* did we modify the file? */ string filetype; /* filetype of the file as string */ string msg; /* message to log */ int lines_mod; /* number of lines modified */ int ix,size,rc; /* counter, file legnth, return code */ /* */ modified = false; /* modified defaults to false */ lines_mod = 0; /* number of lines modified */ filetype = GetFileTypeString(path); /* get filetype of file */ if (is_obfuscatable(filetype)==false){ /* if not a recgnized obscurable file */ return false; /* return false (file not modified) */ } /* */ file = OpenMappedTextFile(path); /* open handle to map data object */ if (IsValidHandle(file)==false){ /* if we could not get a handle */ MessageBox('x',"Cannot open file %s",path); /* display error message */ return false; /* return false (file not modified */ } /* */ size = GetLineCount(file); /* get the length of the file */ for(ix=0;ix<size;ix++){ /* for each line in the file */ line = ReadLine(file,ix); /* read the line */ if (FindInString(line,"ccc")>0){ /* if the line contains the chars 'ccc' */ wp = WordParseCreate(WP_SGML_TAG,line); /* open handle to Wordparser on line */ ccc = WordParseGetWord(wp); /* get the first word */ while (ccc!=""){ /* while the word isn't blank */ if (FindInString(ccc,"ccc")>0 && IsSGMLTag(ccc)){ /* if CCC is in string, and it's SGML */ if(obfuscate_ccc(ccc,ix,file)){ /* replace CCC starting on line ix */ modified = true; /* set modified flag to true */ lines_mod++; /* increment lines modified */ } /* */ break; /* end loop */ } /* */ ccc = WordParseGetWord(wp); /* get the next word in the word parser */ } /* */ } /* */ } /* */ if (modified == true){ /* if we modified the file */ msg = "Obfuscated file: %s"; /* set message to user */ AddMessage(log,msg,path); /* add message to log */ msg = " CCCs Changed: %d"; /* set message to user */ AddMessage(log,msg,lines_mod); /* add message to log */ MappedTextSave(file,path); /* save the file */ } /* */ CloseHandle(file); /* close handle to file */ CloseHandle(wp); /* close the word parser */ return modified; /* return modified status of file (t/f) */ } /* */
One of our new functions, obfuscate_ccc, uses the SGML Parse Object to ensure we look at everything in our CCC XML tag, not just the first line of it. The first thing we do after initializing our SGML parser with SGMLCreate is to figure out what our closing XML tag is going to look like. To snip of any attributes that may be in our open tag, we need to find the first space character in the tag with the FindInString function. If there is no space, we can just use the length of our tag as the spacer value, because it’s going to be the end position of our close tag string. After we use the GetStringSegment function to get a piece of our open tag to act as a close tag, we can use the ReplaceInString function to add a forward slash, making it look like a closing tag. This turns a tag like “<cor:test attr=’test’>" into “</cor:test”, which we can use to find a closing tag. Then we can set the position of the SGML parser to the beginning of the line where we detected our CCC XML tag with the SGMLSetPosition function. Using a while loop and the SGMLNextElement function, we can parse until we get our parser set on the open tag we detected. If our parser stops on an empty element, it means we couldn’t find our open tag, so we should return false and exit.
/****************************************/ boolean obfuscate_ccc(string tag, int sy, handle file){ /* replace the SGML tag at sy in file */ /****************************************/ handle sgml; /* handle to SGML parser */ int rc; /* return code */ int x,y,ex,ey; /* x, y, end x, end y */ int spacer; /* first space char in tag */ string n_tag; /* next tag in the element */ string c_tag; /* closing SGML tag */ boolean modified; /* set default modified flag */ /* */ modified = false; /* reset modified flag */ sgml = SGMLCreate(file); /* get handle to SGML parser */ spacer = FindInString(tag," "); /* find first whitespace in tag */ if (spacer<0){ /* if there's no space */ spacer = GetStringLength(tag); /* set space to full length of tag */ } /* */ c_tag = GetStringSegment(tag,0,spacer); /* get tag up to first space */ c_tag = ReplaceInString(c_tag,"<","</"); /* make closing tag */ n_tag = "init"; /* set to non-blank val to make loop run*/ SGMLSetPosition(sgml,0,sy); /* set to start on start line */ while ((n_tag != tag) && (n_tag!="")){ /* advance to first SGML tag */ n_tag = SGMLNextElement(sgml); /* get next element */ } /* */ if (n_tag == ""){ /* if we cannot find the open tag */ CloseHandle(sgml); /* close SGML parser */ return false; /* return, no change */ } /* */
Now that our parser is set to the beginning of our CCC tag, while the next item does not start with our closing tag string and is not empty, we can test it with the is_ccc function to see if it’s a CCC string or not. If it is, we can get its coordinates and then use the WriteSegment function to overwrite it with our obfuscated version of the CCC. After that, we set the modified flag to true.
If the item is not a CCC, we can just use the SGMLNextItem function to advance the parser to the next item to look at. After we’ve run out of things to parse or encounter an item that begins the same way our closing tag should, we can return our modified variable. It should only be true if we actually changed something in the file.
while((FindInString(n_tag,c_tag)<0) && n_tag!=""){ /* while we have a next tag */ if (is_ccc(n_tag)){ /* if it's a CCC */ x = SGMLGetItemPosSX(sgml); /* get start x pos */ y = SGMLGetItemPosSY(sgml); /* get start y pos */ ex = SGMLGetItemPosEX(sgml); /* get end x pos */ ey = SGMLGetItemPosEY(sgml); /* get end y pos */ WriteSegment(file,obfuscate_string(n_tag),x,y,ex,ey); /* write segment */ modified = true; /* set modified flag */ } /* */ n_tag = SGMLNextItem(sgml); /* advance to next SGML item */ } /* */ return modified; /* return modified value */ } /* */
The UI functions obfuscate_load and obfuscate_validate have gone through a few changes as well. obfuscate_load now loads the value for the recurse and mode options from settings, in addition to the value of the last folder the function was run on. The validate function now gets the value of the recurse checkbox, and, if it’s checked, stores the setting and sets the global value for recursive to true, so any functions running know recursion is enabled.
/****************************************/ void obfuscate_load(){ /* called on dialog load, populate dlg */ /****************************************/ string recurse; /* recursion setting */ string target; /* target folder to obscure */ string mode; /* mode */ /* */ mode = GetSetting("Options","Mode"); /* get mode setting */ target = GetSetting("Options","Last Folder"); /* get the last folder modified */ recurse = GetSetting("Options","Recursive"); /* get recurse setting */ if (mode=="MD_ENCRYPT"){ /* if mode is set to encrypt */ CheckboxSetState(ENCRYPT,BST_CHECKED); /* set checked state */ } /* */ else{ /* if not encrypt */ CheckboxSetState(REDACT,BST_CHECKED); /* set checked state */ } /* */ if (recurse == "true"){ /* if recurse is true */ CheckboxSetState(RECURSIVE,BST_CHECKED); /* set checked state */ } /* */ EditSetText(TARGET,target); /* set the text field on the dialog */ } /* */ /****************************************/ int obfuscate_validate(){ /* called when user presses 'ok' on dlg */ /****************************************/ string target; /* target folder to obscure */ string msg; /* message back to user */ string recurse_state; /* recurse state as string */ int recurse; /* state of recursion checkbox */ int redact, encrypt; /* radio button statuses */ int rc; /* return code */ /* */ recurse_state = "false"; /* state of recurse in file */ recursive = false; /* set recursive to false by default */ target = EditGetText(TARGET); /* get folder from dialog */ if (target == "" || IsPath(target)==false){ /* if it's not a valid path */ msg = "Please choose a valid folder location and try again."; /* set response to user */ MessageBox('x',msg); /* display error to user */ return ERROR_EXIT; /* return with error */ } /* */ redact = CheckboxGetState(REDACT); /* get status of redact radio button */ encrypt = CheckboxGetState(ENCRYPT); /* get status of encrypt radio button */ if (redact == encrypt){ /* if the buttons are the same status */ MessageBox('x',"Please choose an obfuscation mode"); /* display an error */ return ERROR_EXIT; /* return with error */ } /* */ if (redact == BST_CHECKED){ /* if redact is checked off */ msg = "CCC codes are NOT recoverable after redacting. Continue?"; /* make sure user actually means it */ rc = YesNoBox('x',msg); /* by displaying a message */ if (rc!=IDYES){ /* if they didn't press yes */ return rc; /* return their response */ } /* */ obfuscate_mode = MD_REDACT; /* store user selection */ } /* */ else{ /* if mode is not redacted */ obfuscate_mode = MD_ENCRYPT; /* store mode as encrypt */ } /* if no errors returned in validation */ recurse = CheckboxGetState(RECURSIVE); /* get state of recurse checkbox */ if (recurse == BST_CHECKED){ /* if it's checked */ recurse_state = "true"; /* set recurse state string */ recursive = true; /* set recurse global */ } /* */ PutSetting("Options","Recursive",recurse_state); /* store recurse state */ PutSetting("Options","Last Folder",target); /* store last folder obscured */ return ERROR_NONE; /* return no error */ } /* */
This script evolved from a pretty simple example last week into a much more complete solution to protecting sensitive data. With the ability to prompt users to encrypt or redact their CCCs once they’ve live filed a project, this script can help ensure that data is safe. As with anything, there are always further possible areas for improvement. For example, this script identifies only common EDGAR files. It doesn’t look at other extensions or data files that may contain sensitive data but not be in EDGAR format. Some of our users use things like Excel spreadsheets to hold CIK/CCC combinations. This doesn’t have any way of protecting those. Some users may also have .EIS files saved from the EDGAR system. While they are compressed, they are not encrypted and so are vulnerable to being mined for data if accessed by an unauthorized person. This script addresses files that GoFiler regularly uses, but it may need to be tweaked to meet your organization’s specific needs.
Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC. |
Additional Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato