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