1// LICENSE_CODE ZON 2'use strict'; /*jslint browser:true, node:true, es6:true*/ 3(function(){ 4let define; 5const is_node = typeof module=='object' && module.exports && module.children; 6if (!is_node) 7 define = self.define; 8else 9 define = require('../../../util/require_node.js').define(module, '../'); 10define(['/util/sprintf.js', '/util/util.js', '/util/url.js', 11 '/util/version_util.js'], function(sprintf, zutil, zurl, version_util) 12{ 13const E = {}; 14const assign = Object.assign; 15 16E.get_root_domain = zurl.get_root_domain; 17 18E.get_root_url = url=>{ 19 // XXX sergey: quick hack for trial in hola browser 20 if (url=='chrome://newtab/') 21 return 'newtab'; 22 const n = (url||'').match(/^https?:\/\/([^\/]+)(\/.*)?$/); 23 if (!n || !n[1]) 24 return null; 25 return E.get_root_domain(n[1]); 26}; 27 28E.find_rule = (rules, opt)=>{ 29 if (!rules) 30 return; 31 for (let i in rules) 32 { 33 const r = rules[i]; 34 if (opt.name==r.name 35 && (opt.type===undefined || opt.type==r.type) 36 && (opt.md5===undefined || opt.md5==r.md5) 37 && (opt.country===undefined 38 || opt.country.toLowerCase()==r.country.toLowerCase())) 39 { 40 return r; 41 } 42 } 43 return null; 44}; 45 46E.gen_route_str = (route_opt, opt)=>{ 47 opt = opt||{}; 48 if (route_opt.direct) 49 return 'direct'; 50 let s = route_opt.country.toLowerCase(), r = []; 51 if (route_opt.peer) 52 r.push('peer'); 53 if (route_opt.pool) 54 { 55 r.push('pool'+(typeof route_opt.pool=='string' ? 56 '_'+route_opt.pool : '')); 57 } 58 if (!opt.no_algo && route_opt.algo) 59 r.push(route_opt.algo); 60 if (r.length) 61 s += '.'+r.join(','); 62 return s; 63}; 64 65function ip_list_to_agents(ip_list){ 66 return fix_zagent(Object.keys(ip_list).join(',')); 67} 68function fix_zagent(s){ 69 return s.replace(/.hola.org/g, '').replace(/zagent/g, 'za'); 70} 71 72// XXX arik: mv to util? 73function pad(num, size){ return ('000'+num).slice(-size); } 74function time_str(d){ 75 d = new Date(d); 76 if (isNaN(d)) 77 return '00:00:00.000'; 78 return pad(d.getUTCHours(), 2)+':'+pad(d.getUTCMinutes(), 2) 79 +':'+pad(d.getUTCSeconds(), 2) 80 +'.'+pad(d.getUTCMilliseconds(), 3); 81} 82 83E.events_enabled = false; 84E.events = []; 85E.events_n = {}; 86E.reset_events = ()=>{ 87 E.events = []; 88 E.events_n = {}; 89}; 90E.push_event = (name, opt)=>{ 91 try { 92 if (!E.events_enabled) 93 return; 94 if (E.events.length > 512) 95 E.events.splice(0, E.events.length/2); 96 E.events.push({name, ts: Date.now(), opt}); 97 E.events_n[name] = (E.events_n[name]||0)+1; 98 } catch(err){ console.error('push_event error %s', err&&err.stack); } 99}; 100E.enable_events_logging = enabled=>{ 101 E.events_enabled = !!enabled; 102 if (!enabled) 103 E.reset_events(); 104}; 105E.get_event_n = e=>(E.events_n[e]); 106E.events_to_str = ()=>{ 107 let s = '', a =[]; 108 E.events.forEach(e=>{ 109 let s = time_str(e.ts)+' '+e.name, o = e.opt; 110 switch (e.name) 111 { 112 case '>zgettunnels': 113 s += sprintf(' %s%s', o.country, 114 (o.exclude||[]).length ? ' exclude '+ 115 o.exclude.map(e=>fix_zagent(e)).join(',') : ''); 116 break; 117 case '<zgettunnels': 118 s += sprintf(' %s%s', o.country, 119 ' agents '+ip_list_to_agents(o.ip_list)); 120 break; 121 case 'set_rule': 122 s += sprintf(' %s%s%s%s%s', o.name, 123 o.enabled ? ' '+o.country : ' off', 124 o.del ? ' del' : '', 125 o.src ? ' src '+o.src : '', 126 !o.full_vpn ? ' !full_vpn' : '', 127 o.mode=='unblock' ? '' : ' mode '+o.mode); 128 break; 129 case '<verify_err': 130 s += sprintf(' %s %s', o.host ? fix_zagent(o.host) : o.ip, 131 ' '+o.err); 132 break; 133 case '<verify': 134 s += sprintf(' %s %s', o.host ? fix_zagent(o.host) : o.ip, 135 o.rtt ?o.rtt+'ms' : '!rtt'); 136 break; 137 case '>verify': 138 s += sprintf(' %s', o.host ? fix_zagent(o.host) : o.ip); 139 break; 140 case '>stuck': 141 case '>stuck_long': 142 break; 143 case '<stuck_end': 144 s += sprintf(' %sms', o.duration); 145 break; 146 case 'pac_miss': 147 s += sprintf(' %s!=%s %s', o.req_ip, o.agent_ip, o.url); 148 break; 149 case 'pac_reload': 150 s += sprintf(' after %ssec', (o.new_ts - o.init_ts)/1000); 151 break; 152 case 'navgiate': s += sprintf(' tab %s %s', o.tab_id, o.url); break; 153 case 'popup_render': 154 case 'tpopup_render': 155 break; 156 case 'pac_slow':
157 s += sprintf(' %dms %s', o.dur, (o.url||'').substr(0, 50)); 158 break; 159 default: s += JSON.stringify(o)||''; break; 160 } 161 a.push(s); 162 }); 163 a = a.reverse(); 164 return a.join('\n'); 165}; 166 167class Domain_tree { 168 constructor(){ 169 this.clear(); 170 } 171 *[Symbol.iterator](){ 172 let parts = []; 173 for (let item of Domain_tree.iterate_subtree(parts, this.tree)) 174 yield item; 175 } 176 clear(){ 177 this.tree = {}; 178 } 179 get_value(domain){ 180 if (!domain) 181 return; 182 let parts = domain.split('.'); 183 let leaf = this.tree, value = leaf[Domain_tree.VALUE]; 184 for (let i = parts.length-1; leaf && i>=0; i--) 185 { 186 leaf = leaf[parts[i]]; 187 value = leaf && (Domain_tree.VALUE in leaf) ? 188 leaf[Domain_tree.VALUE] : value; 189 } 190 return value; 191 } 192 set_value(domain, value){ 193 if (!domain) 194 return void (this.tree[Domain_tree.VALUE] = value); 195 let parts = domain.split('.'); 196 let leaf = this.tree; 197 for (let i = parts.length-1; i>=0; i--) 198 leaf = leaf[parts[i]] = leaf[parts[i]]||{}; 199 leaf[Domain_tree.VALUE] = value; 200 } 201} 202Domain_tree.VALUE = Symbol.for('value'); 203Domain_tree.iterate_subtree = function*(parts, leaf){ 204 if (Domain_tree.VALUE in leaf) 205 yield [parts.slice().reverse().join('.'), leaf[Domain_tree.VALUE]]; 206 for (let key in leaf) 207 { 208 parts.push(key); 209 for (let item of Domain_tree.iterate_subtree(parts, leaf[key])) 210 yield item; 211 parts.pop(); 212 } 213}; 214 215E.Domain_tree = Domain_tree; 216 217E.get_site_key = (conf, ext_opt, root_url)=>{ 218 let {ext_ver} = ext_opt; 219 let sites = conf && conf.sites || {}; 220 return Object.keys(sites).find(k=>{ 221 let v = sites[k]; 222 let urls = Array.isArray(v.root_url) ? v.root_url : [v.root_url]; 223 return urls.includes(root_url) && E.check_min_ver(ext_ver, v.min_ver); 224 }); 225}; 226 227const get_match_fn = operator=>{ 228 if (operator=='gt') 229 return (a, b)=>a>b; 230 if (operator=='gte') 231 return (a, b)=>a>=b; 232 if (operator=='lt') 233 return (a, b)=>a<b; 234 if (operator=='lte') 235 return (a, b)=>a<=b; 236 if (operator=='re') 237 return (a, b)=>new RegExp(b).test(a); 238 if (operator=='in' || operator=='nin') 239 { 240 return (a, b)=>{ 241 let found; 242 if (!Array.isArray(b)) 243 return false; // bad conf 244 if (!Array.isArray(a)) 245 found = b.includes(a); 246 else 247 found = b.some(i=>a.includes(i)); 248 return operator=='in' ? found : !found; 249 }; 250 } 251 if (operator=='eq' || operator=='neq') 252 { 253 return (a, b)=>{ 254 let equal; 255 if (!Array.isArray(a)) 256 equal = a==b; 257 else if (!Array.isArray(b)) 258 equal = a.includes(b); 259 else 260 equal = b.every(i=>a.includes(i)); 261 return operator=='eq' ? equal : !equal; 262 }; 263 } 264 return (a, b)=>false; 265}; 266 267E.match_filter = (query, filter)=>{ 268 if (!filter) 269 return true; 270 if (!query) 271 return false; 272 query = assign({}, query); 273 for (let key in query) 274 { 275 if (key.endsWith('_ts') && query.now) 276 query['since_'+key] = query.now-query[key]; 277 } 278 for (let f_key in filter) 279 { 280 if (!f_key) 281 continue; 282 let m = f_key.match(/^(.*)_(gt|gte|lt|lte|in|nin|re|eq|neq)$/); 283 let q_key = m ? m[1] : f_key; 284 let operator = m ? m[2] : 'eq'; 285 // fix for filters like {signed_in: true} 286 if (operator=='in' && !Array.isArray(filter[f_key]) || 287 operator=='nin' && !Array.isArray(filter[f_key])) 288 { 289 q_key = f_key; 290 operator = 'eq'; 291 } 292 let op1, op2; 293 if (q_key.endsWith('_ver') && /(gt|gte|lt|lte|eq|neq)/.test(operator)) 294 { 295 op1 = version_util.cmp(query[q_key], filter[f_key]); 296 op2 = 0; 297 } 298 else 299 { 300 op1 = query[q_key]; 301 op2 = filter[f_key]; 302 } 303 if (!get_match_fn(operator)(op1, op2)) 304 return false;
305 } 306 return true; 307}; 308 309const apply_conf_override = (site_conf, ext_opt)=>{ 310 let {ext_ver, install_ver, install_ts} = ext_opt; 311 let {override} = site_conf; 312 site_conf = zutil.omit(site_conf, ['override']); 313 if (!override) 314 return site_conf; 315 let versions = Object.keys(override).sort((a, b)=> 316 -version_util.cmp(a, b)); 317 let ver = versions.find(v=>E.check_min_ver(ext_ver, v) && 318 E.check_min_install_ver(install_ver, override[v].install_min_ver) && 319 E.check_min_install_ts(install_ts, override[v].install_min_ts) 320 ); 321 if (ver) 322 { 323 site_conf = zutil.extend_deep(zutil.clone_deep(site_conf), 324 zutil.omit(override[ver], ['install_min_ver', 'install_min_ts'])); 325 } 326 return site_conf; 327}; 328 329const apply_trial_conf_override = (tconf, ext_opt)=>{ 330 let {override} = tconf; 331 tconf = zutil.omit(tconf, ['override']); 332 if (!override || !ext_opt) 333 return tconf; 334 let {ext_ver, trial_state, now} = ext_opt; 335 let first_start_ts = trial_state && trial_state.first_start_ts; 336 // old ext verssions do not support override 337 if (!first_start_ts || !now || version_util.cmp(ext_ver, '1.174.712')<0) 338 return tconf; 339 let match_opt = assign({first_start_ts}, ext_opt); 340 delete match_opt.trial_state; 341 // XXX sergey: for backward compat. New versions should use since_* syntax 342 match_opt.first_start = now - first_start_ts; 343 // find last match 344 let ov = override.slice().reverse() 345 .find(o=>E.match_filter(match_opt, o.filter)); 346 if (ov) 347 tconf = zutil.extend_deep(zutil.clone_deep(tconf), ov.conf); 348 return tconf; 349}; 350 351E.get_all_sites_conf = (conf, ext_opt)=>{ 352 let {ext_ver, install_ver, install_ts} = ext_opt;
353 let c = conf && conf.sites_default || {}; 354 if (!E.check_min_ver(ext_ver, c.min_ver) || 355 !E.check_min_install_ver(install_ver, c.install_min_ver) || 356 !E.check_min_install_ts(install_ts, c.install_min_ts)) 357 { 358 return; 359 } 360 return apply_conf_override(c, ext_opt); 361}; 362 363E.get_specific_site_conf = (conf, ext_opt, root_url)=>{ 364 let site_conf = zutil.get(conf && conf.sites, E.get_site_key(conf, ext_opt, 365 root_url)); 366 if (site_conf) 367 site_conf = apply_conf_override(site_conf, ext_opt); 368 return site_conf; 369}; 370 371E.get_site_conf = (conf, ext_opt, root_url, path, def)=>{ 372 let site_conf = root_url && 373 E.get_specific_site_conf(conf, ext_opt, root_url) || 374 E.get_all_sites_conf(conf, ext_opt); 375 return path ? zutil.get(site_conf, path, def) : site_conf; 376}; 377 378E.get_trial_conf = (conf, ext_opt, root_url, path, def)=>{ 379 let {ext_ver} = ext_opt; 380 let tconf = E.get_site_conf(conf, ext_opt, root_url, 'trial'); 381 if (!tconf) 382 return; 383 let def_trial = zutil.get(E.get_all_sites_conf(conf, ext_opt), 'trial'); 384 if (tconf.all_sites_timer && !def_trial) 385 tconf = zutil.omit(tconf, ['all_sites_timer']); 386 // if all_sites_timer is enabled, we must use common trial conf for all 387 // sites, but each site can have own skip_signin value 388 if (tconf.all_sites_timer && def_trial) 389 tconf = assign({}, tconf, zutil.omit(def_trial, ['skip_signin'])); 390 tconf = apply_trial_conf_override(tconf, ext_opt); 391 let {time_ended_bonus} = tconf; 392 if (time_ended_bonus && !E.check_min_ver(ext_ver, 393 time_ended_bonus.min_ver)) 394 { 395 tconf = zutil.omit(tconf, ['time_ended_bonus']); 396 } 397 return path ? zutil.get(tconf, path, def) : tconf; 398}; 399 400E.check_min_ver = (ext_ver, min_ver)=> 401 version_util.valid(min_ver) && version_util.cmp(ext_ver, min_ver)>=0; 402 403E.check_min_install_ver = (install_ver, min_ver)=>!min_ver || 404 version_util.valid(min_ver) && version_util.cmp(install_ver, min_ver)>=0; 405 406E.check_min_install_ts = (install_ts, ts)=>!ts || +install_ts>=+ts; 407 408// browser-side twin of svc/lib/rmt_update.js:get_product/pick_version 409// (that one is node-only); see also svc/vpn/peer/util.js:parse_appid 410E.get_product = appid=>{ 411 if (!appid) 412 return undefined; 413 if (appid.startsWith('win_hola.browser_and_vpn.')) 414 return 'browser+vpn'; 415 if (appid.startsWith('win_hola.browser.')) 416 return 'browser'; 417 return 'vpn'; 418}; 419 420E.pick_version = (versions, product, suffix)=>{ 421 if (!product) 422 return versions[suffix]; 423 const keys = product.split('+'); 424 if (keys.length==1) 425 return versions[`${keys[0]}_${suffix}`]; 426 return keys.map(k=>versions[`${k}_${suffix}`]) 427 .reduce((min, v)=>version_util.cmp(v, min)<0 ? v : min); 428}; 429 430return E; }); }());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.