1 /* 2 * Copyright © [2008-2009] Novell, Inc. All Rights Reserved. 3 * 4 * USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE DEVELOPER LICENSE AGREEMENT 5 * OR OTHER AGREEMENT THROUGH WHICH NOVELL, INC. MAKES THE WORK AVAILABLE. THIS WORK 6 * MAY NOT BE ADAPTED WITHOUT NOVELL'S PRIOR WRITTEN CONSENT. 7 * 8 * NOVELL PROVIDES THE WORK "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, 9 * INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR 10 * A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. NOVELL, THE AUTHORS OF THE WORK, AND THE 11 * OWNERS OF COPYRIGHT IN THE WORK ARE NOT LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER 12 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, 13 * OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS IN THE WORK. 14 */ 15 16 17 /** 18 * @fileoverview 19 * This file contains a variety of utility functions used by Collectors 20 * including commonly-used extensions to existing object methods, and 21 * additional SDK object template functions. 22 */ 23 /** 24 * Extensions to the String object methods 25 */ 26 27 /** 28 * Removes whitespace from the beginning and end of the string. 29 * @addon 30 * @return {String} The trimmed string 31 */ 32 String.prototype.trim = function() { 33 return this.replace(/^\s+|\s+$/g,""); 34 }; 35 36 /** 37 * Inserts a string at a specified character position, replacing that character 38 * @example 39 * var str = "this is a string"; 40 * var newstr = str.insert(8, 1, "my"); 41 * 42 * @addon 43 * @param {Number} start Character position in the string at which the inserted string should be placed. 44 * @param {Number} num Number of characters to consume while inserting string 45 * @param {String} ins String to insert into this 46 * @return {String} 47 */ 48 String.prototype.insert = function(start, num, ins) { 49 if ( !(typeof start == "number" && typeof num == "number") || 50 start > this.length || num > this.length ) { return this; } 51 var newstring = this.substr(0,start) + ins.toString() + this.substr(start+num); 52 return newstring; 53 }; 54 55 56 /** 57 * Splits a string into substrings based on a delimiter character. 58 * The safesplit() method works like the split() method, except that it protects 59 * quoted text sections from being split at the delimiter. It will strip quotes from 60 * any token that ends up being an entire output token (and will de-escape embedded 61 * quotes); otherwise it will leave quotes and escapes intact. 62 * <p>Note: does not support the "howmany" argument from split(). 63 * <p>Example: 64 * <pre> 65 * var input1='token1,token2,"token3,including this",token4,token5\\"embedded quote' 66 * var output1=input1.safesplit(); 67 * var input2="this (string) used (parentheses) as quote (chars) \(but not this one\)" 68 * var output2=input2.safesplit(" ","("); 69 * </pre> 70 * @addon 71 * @param {char} delim The delimiter (n char substring) to split the string on - does NOT support empty string 72 * @param {char} quote The quote character; if '[', '(', or '{' are used 73 * this function will look for the natural closing bracket to end a quoted section. 74 * @return {String[]} An output array of token strings 75 */ 76 String.prototype.safesplit = function(delim, quote) { 77 var tokens=[]; 78 tokens[0]=""; 79 if (typeof quote == 'undefined') { quote="\""; } 80 var endquote=quote; 81 if (quote=="(") { endquote=")"; } 82 if (quote=="[") { endquote="]"; } 83 if (quote=="{") { endquote="}"; } 84 if (typeof delim == 'undefined') { delim=","; } 85 var escape="\\"; 86 87 var tokNum=0; 88 var quoting=false; 89 var fullyEnclosed=false; 90 var len=this.length; 91 for (var i = 0; i < len; i++) { 92 if (!quoting) { 93 if (this[i] == quote) { // starting a quote 94 if (tokens[tokNum].length === 0) { // if this is at the beginning of a token, assume it's enclosed 95 quoting = true; 96 fullyEnclosed = true; 97 } 98 else { // otherwise just turn quoting on 99 quoting = true; 100 tokens[tokNum] += this[i]; 101 } 102 } 103 else if (this[i] == escape) { // escape the next char 104 tokens[tokNum] += this[i]; 105 tokens[tokNum] += this[++i]; 106 } 107 else if (this.substr(i,delim.length) == delim) { // delimiter found; create next token 108 tokNum++; 109 tokens[tokNum] = ""; 110 i=i+(delim.length-1); 111 } 112 else { // normal char 113 tokens[tokNum] += this[i]; 114 } 115 } 116 else { // quoting 117 if (this[i] != endquote && this[i] != escape ) { // normal char (delim not used) 118 tokens[tokNum] += this[i]; 119 } 120 else if (this[i] == escape && typeof this[i+1] != 'undefined' && this[i+1] != endquote ) { // not escaping a quote 121 tokens[tokNum] += this[i]; 122 } 123 else if (!fullyEnclosed && this[i] == endquote) { // end of embedded quote 124 tokens[tokNum] += this[i]; 125 quoting=false; 126 } 127 else if (fullyEnclosed && this[i+1] == endquote) { // unescape quotes 128 tokens[tokNum] += this[++i]; 129 } 130 else if (fullyEnclosed && (this.substr(i+1,delim.length) == delim || typeof this[i+1] == 'undefined')) { // we are eliminating end quotes 131 quoting=false; 132 fullyEnclosed=false; 133 i=i+(delim.length-1); 134 } 135 else { // oops, we thought this was enclosed but it was not 136 quoting=false; 137 fullyEnclosed=false; 138 tokens[tokNum] = quote + tokens[tokNum] + endquote; 139 } 140 } 141 } 142 return tokens; 143 }; 144 145 146 /** 147 * The parseNVP() function converts a string in name-value pair format into 148 * a hash where each value can be retrieved by its name. For example, the 149 * string "name=value" would be converted to obj["name"] = value. 150 * <p> 151 * Note that in some cases you may need to pre-process your string to avoid confusion. 152 * Quoting in particular can be thorny, and you'll need to make sure there are no 153 * unquoted characters that look like separators but really aren't. 154 * <p>Example: 155 * <pre> 156 * var input='a=1 b=2 c=hello d="one token"' 157 * input.parseNVP(" ","=",'"') 158 * </pre> 159 * @addon 160 * @param {char} pairSep Character(s) that separate one entry pair from another 161 * @param {char} NVSep Character(s) that separate names from values in a single pair 162 * @param {char} quote Quote character used for values; if '[', '(', or '{' are used 163 * this function will look for the natural closing bracket to end a quoted section. 164 * @return {Hash} Map of names (attributes) to values 165 */ 166 String.prototype.parseNVP = function(pairSep, NVSep, quote) { 167 168 if (typeof NVSep=='undefined') { NVSep="="; } 169 if (typeof pairSep=='undefined') { pairSep=" "; } 170 if (typeof quote=='undefined') { quote="\""; } 171 var ret= {}; 172 var tmparr=[]; 173 174 var entrysplit=this.safesplit(pairSep,quote); 175 for (var i = entrysplit.length-1; i > -1; i--) { 176 tmparr=entrysplit[i].safesplit(NVSep,quote); 177 ret[tmparr[0]]=tmparr[1]; 178 } 179 return ret; 180 }; 181 182 /** 183 * Parse a Syslog string. 184 * Note that this method assumes strict RFC3164 compliance, namely: 185 * Mon DD HH:MM:SS (host) (message) 186 * Also: Syslog messages lack a year, so this method assumes that events were generated 187 * in the current year *except* if the month is December and the current month is January. 188 * @addon 189 * @return {Hash} Three-attribute object with date, host, and message from Syslog string 190 */ 191 String.prototype.parseSyslog = function() { 192 var msg = {}; 193 194 195 196 if (this.search(/^(\w+\s+\d+\s+\d+:\d+:\d+)\s+(\S+)\s+(.*)$/) != -1) { 197 var date_str = RegExp.$1; 198 var sysDate = new Date(); 199 var date_year = sysDate.getFullYear(); 200 201 msg.reporterip = RegExp.$2; 202 msg.message = RegExp.$3; 203 msg.date = Date.parseExact(date_str + " " + date_year, "MMM dd HH:mm:ss yyyy"); 204 // Handle old data from last year 205 if (msg.date.isAfter(sysDate.add({ hours: 2 }))) { 206 msg.date.setFullYear(date_year - 1); 207 } 208 209 // Check for observer's hostname, which will be shifted rightwards 210 if ( msg.message.search(/([a-zA-Z0-9][-a-zA-Z0-9]+[a-zA-Z0-9])\s+(\w+(\[\d+\])?:.*)/) != -1 ) { 211 msg.observerhostname = RegExp.$1; 212 msg.message = RegExp.$2; 213 } 214 } 215 return msg; 216 }; 217 218 /** 219 * Converts a standard path into a normalized form. 220 * This method will take standard Unix and Windows paths and transform them into a normalized form. 221 * It returns this normalized form as an object containing two elements: 222 * this.object : the terminal element in the path, e.g. the name of the actual object 223 * this.context : the slash-separated path which defines the namespace in which that object is contained. 224 * Often these elements will ultimately be assigned to TargetDataName and TargetDataContainer in the output event. 225 * @return {Object} Two-element object that contains an 'object' attribute and a 'context' attribute. 226 */ 227 String.prototype.parsePath = function() { 228 // Types: 1=Unix, 0=Windows 229 230 var path = {}; 231 var type; 232 // If there are no path separators, return just the object name 233 if (this.search(/\//) == -1 && this.search(/\\/) == -1) { 234 path.object = this; 235 return path; 236 } 237 238 /* Next, try to determine if this is Unix or Windows. This is tricky because Unix paths can contain '\' chars 239 * (as escape characters). 240 */ 241 if ( this[0] == "/" || this.search(/\\/) == -1) { 242 type = 1; 243 } else { 244 type = 0; 245 } 246 247 if (type == 1) { 248 var idx = this.lastIndexOf("/"); 249 if (idx > -1) { 250 path.object = this.substr(idx+1); 251 path.context = this.substr(0,idx); 252 } 253 } else { 254 var idx = this.lastIndexOf("\\"); 255 if (idx > -1) { 256 path.object = this.substr(idx + 1); 257 path.context = this.substr(0, idx); 258 path.context = path.context.replace(/\\/, "/"); 259 } 260 } 261 262 return path; 263 }; 264 265 /** 266 * Converts an LDAP-formatted string into an object with separate attributes to contain the 267 * CN and the rest of the directory structure. 268 * This method will take a normal LDAP string (CN=user1.OU=users.OU=engineering.O=novell) and 269 * convert it into an object with separate attributes for the base name and domain (in Sentinel 270 * parlance. This object has two fields: 271 * <ul> 272 * <li>basename: The base common name of the object (ex. "user1") 273 * <li>domain: the rest of the path for the object, in slash notation (ex. "\novell\engineering\users") 274 * </ul> 275 * <p>Example: 276 * <pre> 277 * var ldapuser = "CN=user1.OU=users.OU=engineering.O=novell"; 278 * var ldapobj = ldapuser.parseLDAP(); 279 * // In Rec2Evt.map, modify the lines to say: 280 * InitUserName,ldapobj.basename 281 * InitUserDomain,ldapobj.domain 282 * </pre> 283 * @addon 284 * @return {Hash} Two-attribute object with basename and domain from LDAP string 285 */ 286 String.prototype.parseLDAP = function() { 287 var i = 0; 288 var dn = {}; 289 var splitDN = this.split(","); 290 291 if ( (i = splitDN[0].indexOf("=")) != -1 ) { 292 dn.basename = splitDN[0].substr(i+1); 293 } 294 295 dn.domain = ""; 296 for ( var j = splitDN.length-1; j > 0; j--) { 297 if ( (i = splitDN[j].indexOf("=")) != -1 ) { 298 dn.domain = dn.domain + "\\" + splitDN[j].substr(i+1); 299 } 300 } 301 return dn; 302 }; 303 304 /** 305 * This method converts the string from Base64 encoding to a regular string. 306 * Note that in general you should only attempt to decode strings; decoding binary data 307 * may cause issues. 308 * @example 309 * var string="SGVsbG8sIHdvcmxk"; 310 * var outstring=string.parseBase64(); 311 * @addon 312 */ 313 String.prototype.parseBase64 = function() { 314 315 if (typeof instance.MAPS.base64Map == "undefined") { 316 // define a map for fast lookups 317 instance.MAPS.base64Map = { A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12, 318 N:13,O:14,P:15,Q:16,R:17,S:18,T:19,U:20,V:21,W:22,X:23,Y:24,Z:25, 319 a:26,b:27,c:28,d:29,e:30,f:31,g:32,h:33,i:34,j:35,k:36,l:37,m:38, 320 n:39,o:40,p:41,q:42,r:43,s:44,t:45,u:46,v:47,w:48,x:49,y:50,z:51, 321 "0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63} 322 } 323 var outStr = ""; 324 var incode, outcode, tmpbuf; 325 var count = -1; 326 327 for ( var i=0; i < this.length; i++ ) { 328 incode = instance.MAPS.base64Map[this.charAt(i)]; 329 if(typeof incode != "undefined") { count++; } else { continue; } // skip over whitespace and invalid chars 330 // Break the input into blocks of 4 chars; convert each 6-bit input into 8-bit charcodes 331 switch (count % 4) { 332 case 0: 333 tmpbuf = incode; 334 continue; 335 case 1: 336 outcode = (tmpbuf << 2) | (incode >> 4); 337 tmpbuf = incode & 0x0F; 338 break; 339 case 2: 340 outcode = (tmpbuf << 4) | (incode >> 2); 341 tmpbuf = incode & 0x03; 342 break; 343 case 3: 344 outcode = (tmpbuf << 6) | (incode >> 0); 345 tmpbuf = 0; 346 break; 347 } 348 outStr = outStr + String.fromCharCode(outcode); 349 } 350 351 // Fixup of Finnish chars 352 outStr = outStr.replace(/ä/g, "ä"); 353 outStr = outStr.replace(/ö/g, "ö"); 354 outStr = outStr.replace(/Ã¥/g, "å"); 355 outStr = outStr.replace(/Ä/g, "Ä"); 356 outStr = outStr.replace(/Ö/g, "Ö"); 357 outStr = outStr.replace(/Ã…/g, "Å"); 358 359 return outStr; 360 } 361 362 /** 363 * Extensions to the Date object methods 364 */ 365 366 /** 367 * Converts a JavaScript Date object into a Java Date object 368 * @return {Java Date} Java Date object 369 */ 370 Date.prototype.toJava = function() { 371 return new java.util.Date(this.getTime()); 372 }; 373 374 /** 375 * Extensions to the Array object methods 376 */ 377 378 /** 379 * Converts a JavaScript Array object into a Java array of the named type. 380 * This routine converts a JavaScript Array into a particular type of Java array. For example, 381 * this is the only way to create a byte[] array commonly used for e.g. IP addresses. 382 * <p>Example: 383 * <pre> 384 * var JSArray = [ 012, 000, 000, 0147 ]; 385 * var JavaArray = JSArray.toJava(Byte); 386 * var hostname = java.net.InetAddress.getByAddress(JavaArray); 387 * </pre> 388 * @param {Object[]} type The type of Java array to create 389 */ 390 Array.prototype.toJava = function(type) { 391 var jarr = new java.lang.reflect.Array.newInstance(type ? type : java.lang.Object, this.length); 392 for (var i = 0; i < this.length; ++i) { 393 if (typeof this[i] != "undefined" && this[i] != null) { 394 java.lang.reflect.Array.set(jarr, i, (this[i].toJava ? this[i].toJava(type) : this[i])); 395 } 396 } 397 return jarr; 398 }; 399 400 401 402 /** 403 * log() is a global utility function that allows you to record messages to the log and to events. 404 * @param {String} msg Message to record (required) 405 * @param {Number} sev Numeric severity, use 0-5 (optional) 406 * @param {Flag} dest Destination flag, use instance.LOG, instance.EVENT, or instance.LOG|instance.EVENT (optional, defaults to both) 407 */ 408 log = function(msg, sev, dest) { 409 410 if (typeof dest == "undefined") { dest = 0x3; } 411 var level = Level.FINEST; 412 if( typeof sev == "undefined" ) { 413 if ( typeof instance.CONFIG.params.Error_Message_Severity != 'undefined' ) { 414 sev = instance.CONFIG.params.Error_Message_Severity; 415 } else { 416 sev = 5; 417 } 418 } 419 420 421 if (dest & instance.LOG) { 422 switch (sev) { 423 case 5: 424 level = Level.SEVERE; 425 break; 426 case 4: 427 case 3: 428 level = Level.WARNING; 429 break; 430 case 2: 431 case 1: 432 level = Level.INFO; 433 break; 434 case 0: 435 level = Level.FINE; 436 break; 437 } 438 ScriptEngineUtil.log( level, msg ); 439 } 440 441 // Send an audit event to Sentinel indicating an error in the Collector 442 if (dest & instance.EVENT) { 443 var errEvt = new Event(instance.protoEvt); 444 errEvt.Severity = sev; 445 errEvt.EventName = "Collector Internal Message"; 446 errEvt.SensorType = "A"; 447 errEvt.Message = msg; 448 errEvt.send(instance.MAPS.UnsupEvt); 449 } 450 451 return true; 452 }; 453 454 455 /** 456 * Creates a DataMap object by loading the map from a CSV file on disk. 457 * <p>Example: 458 * <pre> 459 * // First, define the conversion in an external file (CSV format) 460 * Rec2Evt.map: 461 * Message,msg 462 * InitUserName,iun 463 * ... 464 * // Next, load the DataMap: 465 * this.MAPS.Rec2Evt = new DataMap(this.CONFIG.collDir + "/Rec2Evt.map"); 466 * // And set your values in the Record object (typically in Record.parse()) 467 * rec.msg = rec.s_RXBufferString.substr(0,20); 468 * rec.iun = rec.s_RXBufferString.substr(21,25); 469 * // Finally, run the conversion: 470 * rec.convert(this, instance.MAPS.Rec2Evt); 471 * </pre> 472 * @class 473 * A DataMap specifies a conversion from a Record object into some other type of object. 474 * An external file is used to specify how the conversion is accomplished. This external file is 475 * in CSV format, with the first column being the name of the attribute in the target object. 476 * The second column is then the attribute in the Record object that will be plugged into the 477 * target object. The Record attribute references can be complex, for example they can specify 478 * array elements, nested attributes, and so forth. 479 * <p> 480 * In most cases, the conversion will be between a Record object and an Event object. The template 481 * will load the default maps automatically; you will only need to use this class if you are 482 * creating your own output objects or providing multiple conversions. 483 * @param {String} fileName The file to load the DataMap from 484 * @constructor 485 */ 486 function DataMap(fileName){ 487 var mapFile=new File(fileName); 488 var elements=[]; 489 490 if(mapFile.isOpen()) { 491 for (var inString = mapFile.readLine(); typeof inString != 'undefined'; inString = mapFile.readLine()) { 492 elements = inString.safesplit(","); 493 if( typeof elements[0] != "undefined" && typeof elements[1] != "undefined") { 494 elements[0] = elements[0].trim(); 495 elements[1] = elements[1].trim(); 496 if( elements[0] !== "" && elements[0].charAt(0) != "~" && elements[1] !== "" ) { 497 this[elements[0]] = elements[1]; 498 } 499 } 500 } 501 mapFile.close(); 502 } 503 504 /** 505 * Most DataMaps are simple translations between one flat object and another, but in some cases 506 * the source object may be a more complex object. The test here is if the RHS of your DataMap 507 * input file includes any complex object/attribute references, and/or references to "illegal" 508 * attribute names that should be quoted, e.g.: 509 * winevt.evt.myname 510 * My Attribute 511 * obj.Full Name 512 * If you have any of these, you should run the makesafe() method on your DataMap before 513 * attempting to use it. 514 */ 515 this.makesafe = function() { 516 for (var tag in this) { 517 if (typeof this[tag] != "function") { 518 // Escape the various elements of the path 519 var arr = this[tag].safesplit("."); 520 var path = ""; 521 for (var i = 0; i < arr.length; i++) { 522 if ( arr[i].indexOf("[") < 0) { 523 path = path + "[\"" + arr[i] + "\"]"; 524 } else { 525 var tmparr = arr[i].split("["); 526 path = path + "[\"" + tmparr[0] + "\"]"; // root element 527 for (var j = 1; j < tmparr.length; j++) { 528 path = path + "[" + tmparr[j]; // array references 529 } 530 } 531 } 532 this[tag] = path; 533 } 534 } 535 } 536 537 /** 538 * DataMap objects are normally used to convert one object type into another, and the convert() 539 * routine typically uses an eval() to accomplish this. In high-datarate code, however, the 540 * eval() method causes an unacceptable performance hit. 541 * To circumvent this, the compile() method will create a dynamic function which will be used 542 * to perform the conversion instead of the eval(). The normal template will use this for 543 * converting the input Record object into the output Event object. 544 */ 545 this.compile = function() { 546 var body = ""; 547 for (var tag in this) { 548 if (typeof this[tag] != "function") { 549 if (tag == "Severity") { 550 body = body + "try{ typeof input" + this[tag] + " != \"undefined\" ? output." + tag + " = input" + this[tag] + " : true; } catch(err) {};" 551 } else { 552 body = body + "try{input" + this[tag] + " ? output." + tag + " = input" + this[tag] + " : true; } catch(err) {};" 553 } 554 } 555 } 556 var func = "this.preconvert = function(input, output) {" + body + "};"; 557 eval(func); 558 } 559 560 return true; 561 } 562 563 564 /** 565 * Creates a KeyMap object that loads the map from a CSV file on disk. 566 * <p>Example: 567 * <pre> 568 * // First, define your KeyMap input file in CSV format, with the key in the first column 569 * usermap.map: 570 * user1,user1@novell.com,801-861-1000 571 * user2,user2@novell.com,801-861-2000 572 * // Next, load the KeyMap 573 * instance.MAPS.userMap = new KeyMap( instance.CONFIG.collDir + "/usermap.map"); 574 * // And then do your lookups: 575 * var userinfo = instance.MAPS.userMap.lookup(rec.username); 576 * rec.useremail = userinfo[0]; 577 * rec.userphone = userinfo[1]; 578 * // (or you could just put the array references directly in Rec2Evt.map) 579 * </pre> 580 * @class 581 * A KeyMap defines a relationship between a key and a set of strings related to that key. 582 * Each KeyMap is loaded from an input file (CSV format) and can then be used as a reference source 583 * to enhance data gathered by the Collector. For example, you could use the user's name to pull 584 * back the phone number, e-mail address, and other details relating to that user. 585 * The second parameter enable optional header-row parsing; you pass in an ID to tell the 586 * constructor how to find the header row. If you pass in: 587 * <ul> 588 * <li>String: The parser will search for this string at the beginning of each line and treat 589 * that line as the header.</li> 590 * <li>Number: The parser will use this line number as the header row.</li> 591 * </ul> 592 * Note that lines before the header row will be ignored. 593 * @param {String} fileName The name of the file that contains the KeyMap definition. 594 * @param {String or Number} hdrID Tells the constructor how to identify the header row. 595 * @constructor 596 */ 597 function KeyMap(fileName, hdrID){ 598 var mapFile = new File(fileName); 599 this.SourceFiles = []; 600 this.SourceFiles[0] = { 601 "file": fileName, 602 "time": new Date(), 603 "hdrID": hdrID 604 }; 605 if (!mapFile.isOpen()) { 606 return false; 607 } 608 var elements = []; 609 var hdrString = ""; 610 var hdrArr = []; 611 if (typeof hdrID != "undefined" && hdrID != "") { 612 if (typeof hdrID == "number" && hdrID > 1) { 613 for (var i = 1; i < hdrID; i++) { 614 mapFile.readLine(); // discard any lines before header 615 } 616 hdrString = mapFile.readLine(); 617 } 618 else { 619 do { 620 hdrString = mapFile.readLine(); 621 } 622 while (hdrString.indexOf(hdrID) !== 0); 623 } 624 hdrArr = hdrString.split(",").slice(1); 625 this.KeyMapHeader = {}; 626 for (i = hdrArr.length - 1; i > -1; i--) { 627 this.KeyMapHeader[hdrArr[i].trim()] = i; 628 } 629 } 630 for (var inString = mapFile.readLine(); typeof inString != 'undefined'; inString = mapFile.readLine()) { 631 elements = inString.safesplit(","); 632 if (typeof elements[1] != 'undefined') { 633 elements[1] = elements[1].trim(); 634 if (elements[1] !== "" && elements[0].charAt(0) != "~" && elements[0].charAt(0) != "#") { 635 this[elements[0]] = elements.slice(1); 636 } 637 } else if (elements[0] !== "" && elements[0].charAt(0) != "~" && elements[0].charAt(0) != "#") { // single column 638 this[elements[0]] = true; 639 } 640 } 641 mapFile.close(); 642 643 /** 644 * This method extends an existing KeyMap by adding additional keys and values. 645 * The source file must be in the standard CSV format of the original KeyMap file. 646 * A hdrId argument is provided as with the constructor, however this header is just 647 * skipped and the original header will be used to determine column placement (as a 648 * result, the columns must be in the same order). 649 * Note also that if identical keys are specified in the new input map, they will 650 * replace existing entries. 651 * @param {String} fileName The name of the file that contains the KeyMap extension definition. 652 * @param {String or Number} hdrID Tells the constructor how to identify the header row. 653 */ 654 this.extend = function(fileName, hdrID){ 655 var mapFile = new File(fileName); 656 var existingFile; 657 for (i = 0; i < this.SourceFiles.length; i++) { 658 if (this.SourceFiles[i].file == fileName) { 659 existingFile = true; 660 } 661 } 662 if (!existingFile) { 663 this.SourceFiles.push({ 664 "file": fileName, 665 "time": new Date().getTime(), 666 "hdrID": hdrID 667 }); 668 } 669 if (!mapFile.isOpen()) { 670 return false; 671 } 672 var elements = []; 673 var hdrString = ""; 674 var hdrArr = []; 675 if (typeof hdrID != "undefined") { 676 // Read up to header and discard 677 if (hdrID instanceof Number && hdrID > 1) { 678 for (i = 0; i < hdrID; i++) { 679 mapFile.readLine(); 680 } 681 hdrString = mapFile.readLine(); 682 } 683 else { 684 do { 685 hdrString = mapFile.readLine(); 686 } 687 while (hdrString.indexOf(hdrID) !== 0); 688 } 689 } 690 691 for (var inString = mapFile.readLine(); typeof inString != 'undefined'; inString = mapFile.readLine()) { 692 elements = inString.safesplit(","); 693 if (typeof elements[1] != 'undefined') { 694 elements[1] = elements[1].trim(); 695 if (elements[1] !== "" && elements[0].charAt(0) != "~" && elements[0].charAt(0) != "#") { 696 this[elements[0]] = elements.slice(1); 697 } 698 } else if (elements[0] !== "" && elements[0].charAt(0) != "~" && elements[0].charAt(0) != "#") { // single column 699 this[elements[0]] = true; 700 } 701 } 702 mapFile.close(); 703 }; 704 705 /** 706 * Looks up a set of values in a KeyMap with this string as the key. 707 * A KeyMap is used to associate a set of values with a lookup key, sort of like a hash map, but 708 * with multiple possible return strings. You can either fetch the entire array of results, 709 * or select a single column to return with the "col" parameter. If you pass in "col" as: 710 * <ul> 711 * <li>String: Assumes that the KeyMap includes a header row with text column names. Returns the string value of the column with that name.</li> 712 * <li>Number: Returns the string value of of the selected column.</li> 713 * </ul> 714 * Note that the return type depends on whether you use "col" or not; either you get a String 715 * back or an Array of Strings. 716 * @param {String} key The string key to use for the lookup, it must match a string in the first column of the KeyMap 717 * @param {Number or String} col Optional, specifies a single column to return (as array 0). 718 * @return {String[]} An array of strings that were associated with this key in the KeyMap. Null if no match. 719 * @return {String} 720 */ 721 this.lookup = function(key, col){ 722 if (typeof key == "undefined") { 723 return null; 724 } 725 if (typeof col != "undefined") { 726 if (typeof col == 'number') { 727 if (typeof this[key] != "undefined") { 728 return this[key][col]; 729 } else { 730 return null; 731 } 732 } else { 733 return this[key][this.KeyMapHeader[String(col)]]; 734 } 735 } 736 return this[key]; 737 }; 738 739 /** 740 * Checks the KeyMap source file to see if it has been modified, and reloads it if so. 741 */ 742 this.refresh = function(){ 743 // Check last mod time, refresh if needed 744 for (i = 0; i < this.SourceFiles.length; i++) { 745 if (this.SourceFiles[i].time < File.modTime(this.SourceFiles[i].file)) { 746 this.extend(this.SourceFiles[i].file, this.SourceFiles[i].hdrID); 747 } 748 } 749 }; 750 751 }; 752 753 754 /** 755 * Creates a DNS resolver class object. 756 * @class 757 * The DNS class is used to perform hostname and IP address resolution. 758 * <p><strong>Experimental</strong> 759 * @param {String} server The IP address or name of the server to use for lookups. 760 * @constructor 761 */ 762 function DNS(server) { 763 this.server = server; 764 return true; 765 }; 766 767 /** 768 * Resolve a hostname or IP address. 769 * This method will autodetect which format is passed in (host or IP) and return the other. 770 * <p><strong>Experimental</strong> 771 * @param {String} host 772 * @return {String} IP address or hostname that the passed in hostname or IP resolved to. 773 */ 774 DNS.resolve = function(host) { 775 776 var matchIP = new RegExp("\d+\.\d+\.\d+\.\d+"); 777 778 if ( matchIP.test(host) ) { // this is an IP 779 var octets = host.split("."); 780 eval( "var octetArray = [ 0" + octets[0] + ",0" + octets[2] + ",0" + octets[3] + ",0" + octets[4]); 781 var byteArray = octetArray.toJava(Byte); 782 try { 783 // Get hostname by textual representation of IP address 784 var addr = java.net.InetAddress.getByAddress(byteArray); 785 786 // Get canonical host name 787 var hostname = addr.getCanonicalHostName(); 788 789 return hostname; 790 } catch (e) { 791 return "unknown.unknown"; 792 } 793 } else { // we have a hostname 794 try { 795 var addr = java.net.InetAddress.getByName(host); 796 797 var ip = addr; 798 // byte[] ipAddr = addr.getAddress(); 799 800 // Convert to dot representation 801 // String ipAddrStr = ""; 802 // for (int i=0; i<ipAddr.length; i++) { 803 // if (i > 0) { 804 // ipAddrStr += "."; 805 // } 806 // ipAddrStr += ipAddr[i]&0xFF; 807 return ip; 808 809 } catch (err) { 810 return "0.0.0.0"; 811 } 812 } 813 }; 814 815 /** 816 * Adjust a localized date of the current Date object to a newly-specified timezone. 817 * By default, dates created in Javascript are created in the local 818 * timezone, e.g. if I say "create a Date object at time 3PM", what I will 819 * get is a Date that is set to 3PM for the local timezone that this 820 * machine is running in. If our event source sends us the timezone, or 821 * if it is set on the Event Source ESM object, then we want to adjust for 822 * the specified timezone. 823 * This routine uses the Java TimeZone object, so any TZ specifiers supported by that 824 * method will be supported by this routine. 825 * <strong>This method uses the timezone specified in the rec.s_TimeZone variable. This variable 826 * is set by current Connectors if configured.</strong> 827 * If the variable is not set, this code assumes that the TZ of the source device is local time, 828 * and does not change the timezone of this Date object. If your device reports in GMT, you 829 * <strong>must</strong> set the rec.s_TimeZone variable to "UTC". 830 * 831 * @return {Boolean} Specified whether a timezone adjustment was made. 832 */ 833 Date.prototype.adjustTimezone = function() { 834 if( typeof rec.s_TimeZone != "undefined" && rec.s_TimeZone != "") { 835 // Create a Java TimeZone object for this timezone 836 if(typeof instance.MAPS.TZMap[rec.s_TimeZone] == "undefined") { 837 instance.MAPS.TZMap[rec.s_TimeZone] = java.util.TimeZone.getTimeZone(rec.s_TimeZone); 838 } 839 // Convert to UTC so we have the "base time" correct (the offset is calculated against UTC) 840 this.setTimezone("UTC"); 841 this.setTime(this.getTime() - instance.MAPS.TZMap[rec.s_TimeZone].getOffset(this.getTime())); 842 return true; 843 } 844 return false; 845 }; 846 847 /** 848 * Creates a hash value of a string. Message digests are secure one-way hash functions 849 * that take arbitrary-sized data and output a fixed-length hash value. 850 * @param {String} hashtype One of 'MD2','MD5','SHA','SHA-1','SHA-2','SHA-256','SHA-384','SHA-512' specifying the hash algorithm 851 * @return {String} checksum The computed hash value of a string or 'undefined' if it fails due to any reason. 852 */ 853 String.prototype.getHashValue = function(hashtype){ 854 switch (hashtype) { 855 case "MD2": 856 case "MD5": 857 case "SHA-1": 858 case "SHA-384": 859 case "SHA-512": 860 break; 861 case "SHA": 862 case "SHA-2": 863 case "SHA-256": 864 hashtype = "SHA-256"; 865 break; 866 default: 867 return undefined; 868 } 869 try { 870 // Create a Java MessageDigest object for this hashtype message digest algorithm 871 var md5 = new java.security.MessageDigest.getInstance(hashtype); 872 // Create a Java String object for the specified string in 'this'javascript object 873 var str = new java.lang.String(this); 874 // Updates the digest using the specified array of bytes 875 // Use [].concat(<java array object>) to convert java array object to javascript array object 876 md5.update([].concat(str.getBytes())); 877 var checksum = new java.math.BigInteger(1, [].concat(md5.digest())).toString(16); 878 return checksum; 879 } 880 catch (err) { 881 log("Could not generate the hash value of the string " + err, 4, this.LOG); 882 } 883 }; 884 885 886 /** 887 * Converts an IP address in a number of input forms into the standard dotted-quad notation used by Sentinel. 888 * @param {Number} radix The radix of the input; 16 for hex, 8 for octal, etc. Default is 10 for decimal. 889 * @return {String} The converted IP address in normalized, dotted-quad form (network order) 890 */ 891 String.prototype.convertIP = function (radix) { 892 if (typeof radix == "undefined") { var radix = 10; } 893 var inp = parseInt(this, radix); 894 if (inp < 4294967295 ) { 895 var o1 = inp >> 24 & 255; 896 var o2 = inp >> 16 & 255; 897 var o3 = inp >> 8 & 255; 898 var o4 = inp & 255; 899 return o1 + "." + o2 + "." + o3 + "." + o4; 900 } else { 901 return "0.0.0.0"; 902 } 903 }; 904