/*
 * Parsing of pac files
 *
 * CNTLM is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
 * St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Copyright (c) 2022 Francesco MDE aka fralken, David Kubicek
 *
 */

/*
 * https://searchfox.org/mozilla-central/source/netwerk/base/ascii_pac_utils.js
 * https://hg.mozilla.org/mozilla-central/raw-file/tip/netwerk/base/ascii_pac_utils.js
*/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

static const char* pac_utils_js =
"function dnsDomainIs(host, domain) {\n"
"  return (\n"
"    host.length >= domain.length &&\n"
"    host.substring(host.length - domain.length) == domain\n"
"  );\n"
"}\n"

"function dnsDomainLevels(host) {\n"
"  return host.split(\".\").length - 1;\n"
"}\n"

"function isValidIpAddress(ipchars) {\n"
"  var matches = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipchars);\n"
"  if (matches == null) {\n"
"    return false;\n"
"  } else if (\n"
"    matches[1] > 255 ||\n"
"    matches[2] > 255 ||\n"
"    matches[3] > 255 ||\n"
"    matches[4] > 255\n"
"  ) {\n"
"    return false;\n"
"  }\n"
"  return true;\n"
"}\n"

"function convert_addr(ipchars) {\n"
"  var bytes = ipchars.split(\".\");\n"
"  var result =\n"
"    ((bytes[0] & 0xff) << 24) |\n"
"    ((bytes[1] & 0xff) << 16) |\n"
"    ((bytes[2] & 0xff) << 8) |\n"
"    (bytes[3] & 0xff);\n"
"  return result;\n"
"}\n"

"function isInNet(ipaddr, pattern, maskstr) {\n"
"  if (!isValidIpAddress(pattern) || !isValidIpAddress(maskstr)) {\n"
"    return false;\n"
"  }\n"
"  if (!isValidIpAddress(ipaddr)) {\n"
"    ipaddr = dnsResolve(ipaddr);\n"
"    if (ipaddr == null) {\n"
"      return false;\n"
"    }\n"
"  }\n"
"  var host = convert_addr(ipaddr);\n"
"  var pat = convert_addr(pattern);\n"
"  var mask = convert_addr(maskstr);\n"
"  return (host & mask) == (pat & mask);\n"
"}\n"

"function isPlainHostName(host) {\n"
"  return host.search(\"(\\\\.)|:\") == -1;\n"
"}\n"

"function isResolvable(host) {\n"
"  var ip = dnsResolve(host);\n"
"  return ip != null;\n"
"}\n"

"function localHostOrDomainIs(host, hostdom) {\n"
"  return host == hostdom || hostdom.lastIndexOf(host + \".\", 0) == 0;\n"
"}\n"

"function shExpMatch(url, pattern) {\n"
"  pattern = pattern.replace(/\\./g, \"\\\\.\");\n"
"  pattern = pattern.replace(/\\*/g, \".*\");\n"
"  pattern = pattern.replace(/\\?/g, \".\");\n"
"  var newRe = new RegExp(\"^\" + pattern + \"$\");\n"
"  return newRe.test(url);\n"
"}\n"

"var wdays = { SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6 };\n"
"var months = {\n"
"  JAN: 0,\n"
"  FEB: 1,\n"
"  MAR: 2,\n"
"  APR: 3,\n"
"  MAY: 4,\n"
"  JUN: 5,\n"
"  JUL: 6,\n"
"  AUG: 7,\n"
"  SEP: 8,\n"
"  OCT: 9,\n"
"  NOV: 10,\n"
"  DEC: 11,\n"
"};\n"

"function weekdayRange() {\n"
"  function getDay(weekday) {\n"
"    if (weekday in wdays) {\n"
"      return wdays[weekday];\n"
"    }\n"
"    return -1;\n"
"  }\n"
"  var date = new Date();\n"
"  var argc = arguments.length;\n"
"  var wday;\n"
"  if (argc < 1) {\n"
"    return false;\n"
"  }\n"
"  if (arguments[argc - 1] == \"GMT\") {\n"
"    argc--;\n"
"    wday = date.getUTCDay();\n"
"  } else {\n"
"    wday = date.getDay();\n"
"  }\n"
"  var wd1 = getDay(arguments[0]);\n"
"  var wd2 = argc == 2 ? getDay(arguments[1]) : wd1;\n"
"  if (wd1 == -1 || wd2 == -1) {\n"
"    return false;\n"
"  }\n"

"  if (wd1 <= wd2) {\n"
"    return wd1 <= wday && wday <= wd2;\n"
"  }\n"

"  return wd2 >= wday || wday >= wd1;\n"
"}\n"

"function dateRange() {\n"
"  function getMonth(name) {\n"
"    if (name in months) {\n"
"      return months[name];\n"
"    }\n"
"    return -1;\n"
"  }\n"
"  var date = new Date();\n"
"  var argc = arguments.length;\n"
"  if (argc < 1) {\n"
"    return false;\n"
"  }\n"
"  var isGMT = arguments[argc - 1] == \"GMT\";\n"

"  if (isGMT) {\n"
"    argc--;\n"
"  }\n"
"  // function will work even without explict handling of this case\n"
"  if (argc == 1) {\n"
"    var tmp = parseInt(arguments[0]);\n"
"    if (isNaN(tmp)) {\n"
"      return (\n"
"        (isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0])\n"
"      );\n"
"    } else if (tmp < 32) {\n"
"      return (isGMT ? date.getUTCDate() : date.getDate()) == tmp;\n"
"    }\n"
"    return (isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp;\n"
"  }\n"
"  var year = date.getFullYear();\n"
"  var date1, date2;\n"
"  date1 = new Date(year, 0, 1, 0, 0, 0);\n"
"  date2 = new Date(year, 11, 31, 23, 59, 59);\n"
"  var adjustMonth = false;\n"
"  for (var i = 0; i < argc >> 1; i++) {\n"
"    var tmp = parseInt(arguments[i]);\n"
"    if (isNaN(tmp)) {\n"
"      var mon = getMonth(arguments[i]);\n"
"      date1.setMonth(mon);\n"
"    } else if (tmp < 32) {\n"
"      adjustMonth = argc <= 2;\n"
"      date1.setDate(tmp);\n"
"    } else {\n"
"      date1.setFullYear(tmp);\n"
"    }\n"
"  }\n"
"  for (var i = argc >> 1; i < argc; i++) {\n"
"    var tmp = parseInt(arguments[i]);\n"
"    if (isNaN(tmp)) {\n"
"      var mon = getMonth(arguments[i]);\n"
"      date2.setMonth(mon);\n"
"    } else if (tmp < 32) {\n"
"      date2.setDate(tmp);\n"
"    } else {\n"
"      date2.setFullYear(tmp);\n"
"    }\n"
"  }\n"
"  if (adjustMonth) {\n"
"    date1.setMonth(date.getMonth());\n"
"    date2.setMonth(date.getMonth());\n"
"  }\n"
"  if (isGMT) {\n"
"    var tmp = date;\n"
"    tmp.setFullYear(date.getUTCFullYear());\n"
"    tmp.setMonth(date.getUTCMonth());\n"
"    tmp.setDate(date.getUTCDate());\n"
"    tmp.setHours(date.getUTCHours());\n"
"    tmp.setMinutes(date.getUTCMinutes());\n"
"    tmp.setSeconds(date.getUTCSeconds());\n"
"    date = tmp;\n"
"  }\n"
"  return date1 <= date2\n"
"    ? date1 <= date && date <= date2\n"
"    : date2 >= date || date >= date1;\n"
"}\n"

"function timeRange() {\n"
"  var argc = arguments.length;\n"
"  var date = new Date();\n"
"  var isGMT = false;\n"
"  if (argc < 1) {\n"
"    return false;\n"
"  }\n"
"  if (arguments[argc - 1] == \"GMT\") {\n"
"    isGMT = true;\n"
"    argc--;\n"
"  }\n"

"  var hour = isGMT ? date.getUTCHours() : date.getHours();\n"
"  var date1, date2;\n"
"  date1 = new Date();\n"
"  date2 = new Date();\n"

"  if (argc == 1) {\n"
"    return hour == arguments[0];\n"
"  } else if (argc == 2) {\n"
"    return arguments[0] <= hour && hour <= arguments[1];\n"
"  }\n"
"  switch (argc) {\n"
"    case 6:\n"
"      date1.setSeconds(arguments[2]);\n"
"      date2.setSeconds(arguments[5]);\n"
"    // falls through\n"
"    case 4:\n"
"      var middle = argc >> 1;\n"
"      date1.setHours(arguments[0]);\n"
"      date1.setMinutes(arguments[1]);\n"
"      date2.setHours(arguments[middle]);\n"
"      date2.setMinutes(arguments[middle + 1]);\n"
"      if (middle == 2) {\n"
"        date2.setSeconds(59);\n"
"      }\n"
"      break;\n"
"    default:\n"
"      throw new Error(\"timeRange: bad number of arguments\");\n"
"  }\n"

"  if (isGMT) {\n"
"    date.setFullYear(date.getUTCFullYear());\n"
"    date.setMonth(date.getUTCMonth());\n"
"    date.setDate(date.getUTCDate());\n"
"    date.setHours(date.getUTCHours());\n"
"    date.setMinutes(date.getUTCMinutes());\n"
"    date.setSeconds(date.getUTCSeconds());\n"
"  }\n"
"  return date1 <= date2\n"
"    ? date1 <= date && date <= date2\n"
"    : date2 >= date || date >= date1;\n"
"}\n"
;

