<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, len<commit_msg>Cleaned out the intertubes<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, leng<|endoftext|>


<commit_before>/**
 * Created by Shawn Liu on 2014/12/3.
 */
var tools = require("../util/tools");
var site = "twitter.com";
exports.registration = function (casper, result, timeout, callback) {
    var data = result.postData;
    if (data) {
        casper.then(function () {
            _registration(casper, data, result, timeout, callback);
        });

        casper.then(function () {

        });

    } else {
        result.status = false;
        result.message = "missed required fields";
    }
}

function _registration(casper, formData, result, timeout, callback) {

    casper.wait(tools.random(20, 90), function () {

    });
}
exports.edit = function (casper, result, timeout, callback) {

}

exports.operation = function (casper, result, timeout, callback) {
    var data = result.postData;
    var ops = data.operation;
    result.dts = ops;
    if (ops && ops.length > 0) {
        try {
            _login(casper, data, result, timeout, callback, function () {//login before do any operation
                function _repeat(time) {
                    if (time <= 0) {
                        return;
                    } else {
                        casper.wait(1000, function () {
                            _handleOperation(ops[time - 1], data.email, function finish() {
                                time--;
                                _repeat(time);
                            }, callback, result, timeout, casper)
                        });
                    }
                }

                _repeat(ops.length);
                result.status = true;
                result.message = "success to finish all operations";
            });

        } catch (e) {
            result.message = e;
        }

        callback(result);
    } else {
        result.status = false;
        result.message = "Didn't find any operations need to he handled";
    }

}

function _handleOperation(op, email, callback, errorCallback, result, timeout, casper) {
    switch (op.type) {
        case "followByFollowers" :
        {
            _followByFollowers(casper, email, result, timeout, callback);
            break;
        }
        case "followBySearch":
        {
            _followBySearch(casper, email, result, timeout, callback);
            break;
        }
        default :
        {
            result.status = false;
            result.message = "unkown operation '" + op.type + "'";
            errorCallback(result);
        }
    }

}

function _followByFollowers(casper, email, result, timeout, callback) {
    var followButtonS = "#page-container > div.dashboard.dashboard-left.home-exp-tweetbox > div.DashboardProfileCard.module > div > div.DashboardProfileCard-stats > ul > li:nth-child(3) > a > span.DashboardProfileCard-statLabel.u-block";
    casper.waitUntilVisible(followButtonS, function () {
        casper.click(followButtonS);
        casper.then(function () {
            tools.getScreenShot(casper, site, email, "beginToFollow");
            casper.waitUntilVisible(".GridTimeline-items", function () {
                var total = casper.fetchText("#page-container > div.ProfileCanopy.ProfileCanopy--withNav.ProfileCanopy--large > div > div.ProfileCanopy-navBar > div.AppContainer > div > div.Grid-cell.u-size2of3.u-lg-size3of4 > div > div > ul > li.ProfileNav-item.ProfileNav-item--followers.is-active > a > span.ProfileNav-value");
                total = parseInt(total);
                function _follow(time) {

                    casper.waitUntilVisible("#stream-item-user-2872488593 > div:nth-child(1) > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > button:nth-child(2)", function () {
                        tools.getScreenShot(casper, site, email, "followTimes" + time);
                        casper.wait(tools.random(20, 90) * 1000, function () {
                            time = parseInt(time);
                            casper.click("GridTimeline-items .user-actions-follow-button .following-text");
                            //result.buttonLength = this.evaluate(function (index) {
                            //    var buttons = document.querySelectorAll(".GridTimeline-items .user-actions-follow-button .following-text");
                            //    buttons[0].click();
                            //    return buttons ? buttons.length : 0;
                            //}, time);
                            //casper.wait(2000, function () {
                            //    casper.capture("AfterClickFollow" + time + ".png");
                            //    time--;
                            //    if (time === 0) {
                            //        return;
                            //    } else {
                            //        _follow(time);
                            //    }
                            //});
                            if (casper.exists(".GridTimeline-items .user-actions-follow-button:nth-child(" + time + ") .following-text")) {
                                casper.click(".GridTimeline-items .user-actions-follow-button:nth-child(" + time + ") .following-text");
                                time--;
                                if (time === 0) {
                                    return;
                                } else {
                                    _follow(time);
                                }
                            } else {
                                result.message = "follow button doesn't exist";
                                result.status = false;
                                tools.getScreenShot(casper, site, email, "followButtonNotfound");
                                callback(result);
                            }

                        });
                    }, function () {
                        result.message = "wait timeout for follow button selector";
                        result.status = false;
                        tools.getScreenShot(casper, site, email, "followButtonTimeout");
                        callback(result);
                    }, timeout);

                }

                _follow(total);
            }, function () {
                result.status = false;
                result.message = "Wait timeout when waiting all follower appear.";
                tools.getScreenShot(casper, data.site, "followByFollowersFailed");
                callback(result);
            }, timeout)
        })
    }, function () {
        result.message = "can not find follow button on home page";
        callback(result);
    }, timeout);

}

function _followBySearch(casper, email, text, result, timeout, callback) {
    casper.waitUntilVisible("#search-query", function () {
        this.sendKeys("#search-query", text);
        casper.thenClick("#global-nav-search > span > button");
        casper.then(function () {
            casper.waitUntilVisible("#content-main-heading", function () {
                tools.getScreenShot(casper, site, email, "followBySearchSuccess");
                //casper.click("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button");
                if (casper.exists("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button")) {
                    casper.click("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button");
                } else {
                    result.message = "follow button doesn't exist after search";
                    callback(result);
                }
            }, function () {
                result.status = false;
                result.message = "wait timeout for selector '#global-nav-search > span > button'";
                tools.getScreenShot(casper, site, email, "searchError");
            }, timeout);
        });
    }, function () {
        result.status = false;
        result.message = "wait timeout for selector '#search-query'";
        tools.getScreenShot(casper, "twitter.com", "searchError");
    }, timeout)
}

function _login(casper, data, result, timeout, callback, executeCallback) {
    site = data.site;
    casper.capture("beginLogin.png");
    var loginButton = "#front-container > div.front-card > div.front-signin.js-front-signin > form > table > tbody > tr > td.flex-table-secondary > button";
    casper.waitUntilVisible(loginButton, function () {
        //casper.sendKeys("#signin-email", data.email);
        //casper.sendKeys("#signin-password", data.password);
        casper.sendKeys("#signin-email", "sdfsdf@t1.com");
        casper.sendKeys("#signin-password", "qweasd123");
        casper.thenClick(loginButton, function () {
            casper.waitWhileVisible(loginButton, function () {
                if (casper.exists("#page-container > div > div.signin-wrapper > form > div.clearfix > button")) {
                    result.status = false;
                    result.data = data;
                    result.message = "username or password wrong";
                    tools.getScreenShot(casper, data.site, data.email, "loginFailed");
                    callback(result);
                } else {
                    tools.getScreenShot(casper, data.site, data.email, "loginSuccess");
                    executeCallback();
                }
            }, function () {
                result.status = false;
                result.message = "username or password wrong1";
                tools.getScreenShot(casper, data.site, data.email, "loginButtonMissed");
                callback(result);
            }, timeout);
        });
    }, function () {
        capsper.log("can't find email input . assume no need login", "error");
        tools.getScreenShot(casper, data.site, data.email, "noNeedLogin");
        executeCallback();// no need login anymore
    }, timeout);

}

function _validateRegistration(data) {
    var re = {
        status: true,
        "message": []
    }
    if (data.fullName) {
        re.status = false;
        re.message.push("FullName is required");
    }
    if (data.email) {
        re.status = false;
        re.message.push("Email is required");
    }
    if (data.password) {
        re.status = false;
        re.message.push("Password is required");
    }
    if (data.userName) {
        re.status = false;
        re.message.push("userName is required");
    }
    return re;
}<commit_msg>success to follow all one time<commit_after>/**
 * Created by Shawn Liu on 2014/12/3.
 */
var tools = require("../util/tools");
var site = "twitter.com";
exports.registration = function (casper, result, timeout, callback) {
    var data = result.postData;
    if (data) {
        casper.then(function () {
            _registration(casper, data, result, timeout, callback);
        });

        casper.then(function () {

        });

    } else {
        result.status = false;
        result.message = "missed required fields";
    }
}

function _registration(casper, formData, result, timeout, callback) {

    casper.wait(tools.random(20, 90), function () {

    });
}
exports.edit = function (casper, result, timeout, callback) {

}

exports.operation = function (casper, result, timeout, callback) {
    var data = result.postData;
    var ops = data.operation;
    if (ops && ops.length > 0) {
        try {
            _login(casper, data, result, timeout, callback, function () {//login before do any operation
                function _repeat(time) {
                    if (time <= 0) {
                        return;
                    } else {
                        casper.wait(1000, function () {
                            _handleOperation(ops[time - 1], data.email, function finish() {
                                time--;
                                _repeat(time);
                            }, callback, result, timeout, casper)
                        });
                    }
                }

                _repeat(ops.length);
                result.status = true;
                result.message = "success to finish all operations";
            });

        } catch (e) {
            result.message = e;
        }

        callback(result);
    } else {
        result.status = false;
        result.message = "Didn't find any operations need to he handled";
    }

}

function _handleOperation(op, email, callback, errorCallback, result, timeout, casper) {
    switch (op.type) {
        case "followByFollowers" :
        {
            _followByFollowers(casper, email, result, timeout, callback);
            break;
        }
        case "followBySearch":
        {
            _followBySearch(casper, email, result, timeout, callback);
            break;
        }
        default :
        {
            result.status = false;
            result.message = "unkown operation '" + op.type + "'";
            errorCallback(result);
        }
    }

}

function _followByFollowers(casper, email, result, timeout, callback) {
    var followButtonS = "#page-container > div.dashboard.dashboard-left.home-exp-tweetbox > div.DashboardProfileCard.module > div > div.DashboardProfileCard-stats > ul > li:nth-child(3) > a > span.DashboardProfileCard-statLabel.u-block";
    casper.waitUntilVisible(followButtonS, function () {
        casper.click(followButtonS);
        casper.then(function () {
            tools.getScreenShot(casper, site, email, "beginToFollow");
            casper.waitUntilVisible(".GridTimeline-items", function () {
                var total = this.evaluate(function () {
                    var nodes = document.querySelectorAll(".GridTimeline-items .Icon--follow");
                    return nodes ? nodes.length : 0;
                })

                function _follow(time) {
                    casper.wait(timeout, function () {

                        tools.getScreenShot(casper, site, email, "followTimes" + time);
                        casper.wait(tools.random(20, 90) * 1000, function () {
                            time = parseInt(time);
                            //casper.click(".GridTimeline-items .user-actions-follow-button .Icon--follow");
                            //result.buttonLength = this.evaluate(function (index) {
                            //    var buttons = document.querySelectorAll(".GridTimeline-items .user-actions-follow-button .following-text");
                            //    buttons[0].click();
                            //    return buttons ? buttons.length : 0;
                            //}, time);
                            //casper.wait(2000, function () {
                            //    casper.capture("AfterClickFollow" + time + ".png");
                            //    time--;
                            //    if (time === 0) {
                            //        return;
                            //    } else {
                            //        _follow(time);
                            //    }
                            //});
                            if (casper.exists(".GridTimeline-items .Icon--follow:nth-child(" + time + ")")) {
                                casper.click(".GridTimeline-items .Icon--follow:nth-child(" + time + ")");
                                time--;
                                if (time === 0) {
                                    return;
                                } else {
                                    _follow(time);
                                }
                            } else {
                                result.message = "follow button doesn't exist";
                                result.status = false;
                                tools.getScreenShot(casper, site, email, "followButtonNotfound");
                                callback(result);
                            }

                        });

                    }, function () {
                        result.message = "wait timeout for follow button selector";
                        result.status = false;
                        tools.getScreenShot(casper, site, email, "followButtonTimeout");
                        callback(result);
                    }, timeout);

                }

                if (total > 0) {
                    _follow(total);
                }

            }, function () {
                result.status = false;
                result.message = "Wait timeout when waiting all follower appear.";
                tools.getScreenShot(casper, data.site, "followByFollowersFailed");
                callback(result);
            }, timeout)
        })
    }, function () {
        result.message = "can not find follow button on home page";
        callback(result);
    }, timeout);

}

function _followBySearch(casper, email, text, result, timeout, callback) {
    casper.waitUntilVisible("#search-query", function () {
        this.sendKeys("#search-query", text);
        casper.thenClick("#global-nav-search > span > button");
        casper.then(function () {
            casper.waitUntilVisible("#content-main-heading", function () {
                tools.getScreenShot(casper, site, email, "followBySearchSuccess");
                //casper.click("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button");
                if (casper.exists("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button")) {
                    casper.click("ol.big-avatar-list li:nth-child(1) button.user-actions-follow-button");
                } else {
                    result.message = "follow button doesn't exist after search";
                    callback(result);
                }
            }, function () {
                result.status = false;
                result.message = "wait timeout for selector '#global-nav-search > span > button'";
                tools.getScreenShot(casper, site, email, "searchError");
            }, timeout);
        });
    }, function () {
        result.status = false;
        result.message = "wait timeout for selector '#search-query'";
        tools.getScreenShot(casper, "twitter.com", "searchError");
    }, timeout)
}

function _login(casper, data, result, timeout, callback, executeCallback) {
    site = data.site;
    casper.capture("beginLogin.png");
    var loginButton = "#front-container > div.front-card > div.front-signin.js-front-signin > form > table > tbody > tr > td.flex-table-secondary > button";
    casper.waitUntilVisible(loginButton, function () {
        casper.sendKeys("#signin-email", data.email);
        casper.sendKeys("#signin-password", data.password);
        //casper.sendKeys("#signin-email", "sdfsdf@t1.com");
        //casper.sendKeys("#signin-password", "qweasd123");
        casper.thenClick(loginButton, function () {
            casper.waitWhileVisible(loginButton, function () {
                if (casper.exists("#page-container > div > div.signin-wrapper > form > div.clearfix > button")) {
                    result.status = false;
                    result.message = "username or password wrong";
                    tools.getScreenShot(casper, data.site, data.email, "loginFailed");
                    callback(result);
                } else {
                    tools.getScreenShot(casper, data.site, data.email, "loginSuccess");
                    executeCallback();
                }
            }, function () {
                result.status = false;
                result.message = "username or password wrong1";
                tools.getScreenShot(casper, data.site, data.email, "loginButtonMissed");
                callback(result);
            }, timeout);
        });
    }, function () {
        capsper.log("can't find email input . assume no need login", "error");
        tools.getScreenShot(casper, data.site, data.email, "noNeedLogin");
        executeCallback();// no need login anymore
    }, timeout);

}

function _validateRegistration(data) {
    var re = {
        status: true,
        "message": []
    }
    if (data.fullName) {
        re.status = false;
        re.message.push("FullName is required");
    }
    if (data.email) {
        re.status = false;
        re.message.push("Email is required");
    }
    if (data.password) {
        re.status = false;
        re.message.push("Password is required");
    }
    if (data.userName) {
        re.status = false;
        re.message.push("userName is required");
    }
    return re;
}<|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile<commit_msg>Reverting changes<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile]<|endoftext|>


<commit_before>"use strict";

var Core = require("lapis-core/index.js");
var Data = require("lazuli-data/index.js");
var UI = require("lazuli-ui/index.js");

/**
* To represent a component of a page with display content
*/

module.exports = Core.Base.clone({
    id: "Section",
    visible: true,
    width: "100%",
    tb_span: 12,        // replacing width
    right_align_numbers: false,
    allow_panel_expand: true,
    allow_panel_reload: false,
    allow_panel_collapse: true,
    allow_panel_remove: true,
});

module.exports.register("setup");
module.exports.register("update");
module.exports.register("presave");
module.exports.register("success");
module.exports.register("failure");
module.exports.register("cancel");
module.exports.register("render");

/**
* Inheritance hook for one-time initialisation logic for this page section; called by x.Page.setup()
* before setupCall()
*/
module.exports.defbind("setEntity", "setup", function () {
    if (typeof this.entity === "string" && !this.entity_id) {       // backward-compatibility - use entity_id instead of entity to specify string going forwards
        this.entity_id = this.entity;
    }
    if (typeof this.entity !== "object" && this.entity_id) {        // any section that uses a primary entity object can either specify entity_id or call overrideEntity() in page::setupStart
        this.entity = Data.entities.getThrowIfUnrecognized(this.entity_id);
    }
});

module.exports.defbind("setAccessible", "setup", function () {
    var allowed = { access: false, };
    if (this.apply_entity_security && this.entity) {
        if (this.entity.security) {
            this.owner.page.session.checkSecurityLevel(this.entity.security, allowed, "entity");
        }
        if (!allowed.found && this.entity.area) {
            this.owner.page.session.checkSecurityLevel(
                Data.areas.getThrowIfUnrecognized(this.entity.area).security, allowed, "area");
        }
        this.accessible = allowed.access;
    }
});

module.exports.define("overrideEntity", function (entity_id) {
    // not using getRecord() - don't want an instance...
    this.entity = Data.entities.getThrowIfUnrecognized(entity_id).clone({
        id: entity_id,
        skip_registration: true,
    });
    return this.entity;
});

/**
* Begin render logic for this page section, call this.getSectionElement() to create the other div
* for the section, unless this.hide_section_if_empty is set to suppress this; called by
* Page.renderSections() is this.visible and other tab-related logic
* @param x.XmlStream object representing the section-containing div; 'render_opts' object map that
* controls aspects of page appearance
*/
module.exports.define("render", function (element, render_opts) {
    this.sctn_elem = null;
    this.parent_elem = element;
    if (!this.hide_section_if_empty) {
        this.getSectionElement(render_opts);
    }
    this.happen("render", render_opts);
});

/**
* To output the opening elements of the section on first call - the outer div, its title and
* introductory text, and sets this.sctn_elem which is used by subsequent render logic for the
* section; can be called repeatedly to return this.sctn_elem
* @param 'render_opts' object map that controls aspects of page appearance
* @return x.XmlStream object representing the main div of the section, to which subsequent
* content should be added
*/
module.exports.define("getSectionElement", function (render_opts) {
    if (!this.sctn_elem) {
        this.sctn_elem = this.makePanelSectionElement(render_opts);
    }
    return this.sctn_elem;
});

module.exports.define("makePanelSectionElement", function (render_opts) {
    var outer_div_elmt = this.parent_elem
        .makeElement("div", this.getCSSClass(), this.id)
        .makeElement("div", "panel");     /* panel-inverse */
    var heading_elmt = outer_div_elmt.makeElement("div", "panel-heading");
    var button_grp_elmt = heading_elmt.makeElement("div", "panel-heading-btn");
    var sctn_elmt;

    if (this.allow_panel_expand) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-default")
            .attr("data-click", "panel-expand")
            .makeElement("i", "fa fa-expand");
    }
    if (this.allow_panel_reload) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-success")
            .attr("data-click", "panel-reload")
            .makeElement("i", "fa fa-repeat");
    }
    if (this.allow_panel_collapse) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-warning")
            .attr("data-click", "panel-collapse")
            .makeElement("i", "fa fa-minus");
    }
    if (this.allow_panel_remove) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-danger")
            .attr("data-click", "panel-remove")
            .makeElement("i", "fa fa-times");
    }
    this.makeSectionTitle(heading_elmt, render_opts);
    sctn_elmt = outer_div_elmt.makeElement("div", "panel-body");
    this.makeSectionText(sctn_elmt, render_opts);
    this.makeSectionStyle(sctn_elmt, render_opts);
    return sctn_elmt;
});

module.exports.define("makeSectionTitle", function (heading_elmt, render_opts) {
    var temp_title = this.title || this.generated_title;
    if (temp_title) {
        heading_elmt.makeElement("h4", "panel-title")
            .text(temp_title);
    }
});

module.exports.define("makeSectionText", function (sctn_elmt, render_opts) {
    if (this.text) {
        sctn_elmt.makeElement("div", "css_section_text")
            .text(this.text, true);    // Valid XML content
    }
});

module.exports.define("makeSectionStyle", function (sctn_elmt, render_opts) {
    if (this.scoped_style) {
        sctn_elmt.makeElement("style")
            .attr("scoped", "scoped")
            .text(this.scoped_style, true, true);
    }
});

/**
* To determine the CSS class(es) for the div element of this page, including its tb_span, and
* whether or not numbers should be right-aligned",
* @param 'render_opts' object map that controls aspects of page appearance",
* @return String content of the div element's CSS class attribute"
*/
module.exports.define("getCSSClass", function (render_opts) {
    var css_class = "css_section css_section_" + (this.css_type_override || this.type) + " col-md-" + this.tb_span;
    if (this.right_align_numbers) {
        css_class += " css_right_align_numbers";
    }
    return css_class;
});

/**
* To report whether or not this section is entirely valid, to be overridden",
* @param none",
* @return true (to be overridden)"
*/
module.exports.define("isValid", function () {
    return true;
});

/**
* To return the URL parameters to include in order to reference back to this section object",
* @param none",
* @return String URL fragment, beginning with '&'"
*/
module.exports.define("getReferURLParams", function () {
    return "&refer_page=" + this.owner.page.id + "&refer_section=" + this.id;
});

module.exports.define("deduceKey", function () {
    var key;
    var link_field;

    if (this.key) {                         // key specified directly as a property
        key = this.key;
    } else if (this.link_field) {           // via 'link_field' property
        link_field = this.owner.page.getPrimaryRow().getField(this.link_field);
        if (!link_field) {
            this.throwError("link field invalid");
        }
        key = link_field.get();
    } else if (this.owner.page.page_key_entity) {       // having same entity as page_key_entity
        if (this.entity.id === this.owner.page.page_key_entity.id && this.owner.page.page_key) {
            key = this.owner.page.page_key;
        }
    // having same key as page
    } else if (this.entity.id === this.owner.page.entity.id && this.owner.page.page_key) {
        key = this.owner.page.page_key;
    }
    return key;
});

/**
* Create a new section object in the owning page, using the spec properties supplied
* @param Spec object whose properties will be given to the newly-created section
* @return Newly-created section object
*/
UI.Page.sections.override("add", function (spec) {
    var section;
    if (!spec.type) {
        this.throwError("Section type must be specified in spec: " + spec.id);
    }
    section = UI.sections.getThrowIfUnrecognized(spec.type).clone(spec);
    Core.OrderedMap.add.call(this, section);
    return section;
});

<commit_msg>support switchable flexbox section layout<commit_after>"use strict";

var Core = require("lapis-core/index.js");
var Data = require("lazuli-data/index.js");
var UI = require("lazuli-ui/index.js");

/**
* To represent a component of a page with display content
*/

module.exports = Core.Base.clone({
    id: "Section",
    visible: true,
    width: "100%",
    tb_span: 12,        // replacing width
    right_align_numbers: false,
    allow_panel_expand: true,
    allow_panel_reload: false,
    allow_panel_collapse: true,
    allow_panel_remove: true,
});

module.exports.register("setup");
module.exports.register("update");
module.exports.register("presave");
module.exports.register("success");
module.exports.register("failure");
module.exports.register("cancel");
module.exports.register("render");

/**
* Inheritance hook for one-time initialisation logic for this page section; called by x.Page.setup()
* before setupCall()
*/
module.exports.defbind("setEntity", "setup", function () {
    if (typeof this.entity === "string" && !this.entity_id) {       // backward-compatibility - use entity_id instead of entity to specify string going forwards
        this.entity_id = this.entity;
    }
    if (typeof this.entity !== "object" && this.entity_id) {        // any section that uses a primary entity object can either specify entity_id or call overrideEntity() in page::setupStart
        this.entity = Data.entities.getThrowIfUnrecognized(this.entity_id);
    }
});

module.exports.defbind("setAccessible", "setup", function () {
    var allowed = { access: false, };
    if (this.apply_entity_security && this.entity) {
        if (this.entity.security) {
            this.owner.page.session.checkSecurityLevel(this.entity.security, allowed, "entity");
        }
        if (!allowed.found && this.entity.area) {
            this.owner.page.session.checkSecurityLevel(
                Data.areas.getThrowIfUnrecognized(this.entity.area).security, allowed, "area");
        }
        this.accessible = allowed.access;
    }
});

module.exports.define("overrideEntity", function (entity_id) {
    // not using getRecord() - don't want an instance...
    this.entity = Data.entities.getThrowIfUnrecognized(entity_id).clone({
        id: entity_id,
        skip_registration: true,
    });
    return this.entity;
});

/**
* Begin render logic for this page section, call this.getSectionElement() to create the other div
* for the section, unless this.hide_section_if_empty is set to suppress this; called by
* Page.renderSections() is this.visible and other tab-related logic
* @param x.XmlStream object representing the section-containing div; 'render_opts' object map that
* controls aspects of page appearance
*/
module.exports.define("render", function (element, render_opts) {
    this.sctn_elem = null;
    this.parent_elem = element;
    if (!this.hide_section_if_empty) {
        this.getSectionElement(render_opts);
    }
    this.happen("render", render_opts);
});

/**
* To output the opening elements of the section on first call - the outer div, its title and
* introductory text, and sets this.sctn_elem which is used by subsequent render logic for the
* section; can be called repeatedly to return this.sctn_elem
* @param 'render_opts' object map that controls aspects of page appearance
* @return x.XmlStream object representing the main div of the section, to which subsequent
* content should be added
*/
module.exports.define("getSectionElement", function (render_opts) {
    if (!this.sctn_elem) {
        this.sctn_elem = this.makePanelSectionElement(render_opts);
    }
    return this.sctn_elem;
});

module.exports.define("makePanelSectionElement", function (render_opts) {
    var outer_div_elmt = this.parent_elem
        .makeElement("div", this.getCSSClass(), this.id)
        .makeElement("div", "panel");     /* panel-inverse */
    var heading_elmt = outer_div_elmt.makeElement("div", "panel-heading");
    var button_grp_elmt = heading_elmt.makeElement("div", "panel-heading-btn");
    var sctn_elmt;

    if (this.allow_panel_expand) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-default")
            .attr("data-click", "panel-expand")
            .makeElement("i", "fa fa-expand");
    }
    if (this.allow_panel_reload) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-success")
            .attr("data-click", "panel-reload")
            .makeElement("i", "fa fa-repeat");
    }
    if (this.allow_panel_collapse) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-warning")
            .attr("data-click", "panel-collapse")
            .makeElement("i", "fa fa-minus");
    }
    if (this.allow_panel_remove) {
        button_grp_elmt.makeElement("a", "btn btn-xs btn-icon btn-circle btn-danger")
            .attr("data-click", "panel-remove")
            .makeElement("i", "fa fa-times");
    }
    this.makeSectionTitle(heading_elmt, render_opts);
    sctn_elmt = outer_div_elmt.makeElement("div", "panel-body");
    this.makeSectionText(sctn_elmt, render_opts);
    this.makeSectionStyle(sctn_elmt, render_opts);
    return sctn_elmt;
});

module.exports.define("makeSectionTitle", function (heading_elmt, render_opts) {
    var temp_title = this.title || this.generated_title;
    if (temp_title) {
        heading_elmt.makeElement("h4", "panel-title")
            .text(temp_title);
    }
});

module.exports.define("makeSectionText", function (sctn_elmt, render_opts) {
    if (this.text) {
        sctn_elmt.makeElement("div", "css_section_text")
            .text(this.text, true);    // Valid XML content
    }
});

module.exports.define("makeSectionStyle", function (sctn_elmt, render_opts) {
    if (this.scoped_style) {
        sctn_elmt.makeElement("style")
            .attr("scoped", "scoped")
            .text(this.scoped_style, true, true);
    }
});

/**
* To determine the CSS class(es) for the div element of this page, including its tb_span, and
* whether or not numbers should be right-aligned",
* @param 'render_opts' object map that controls aspects of page appearance",
* @return String content of the div element's CSS class attribute"
*/
module.exports.define("getCSSClass", function (render_opts) {
    var css_class = "css_section css_section_" + (this.css_type_override || this.type);
    if (this.owner.page.flexbox_section_layout) {
        css_class += " fb-item-" + this.tb_span;
    } else {
        css_class += " col-md-" + this.tb_span;
    }
    if (this.right_align_numbers) {
        css_class += " css_right_align_numbers";
    }
    return css_class;
});

/**
* To report whether or not this section is entirely valid, to be overridden",
* @param none",
* @return true (to be overridden)"
*/
module.exports.define("isValid", function () {
    return true;
});

/**
* To return the URL parameters to include in order to reference back to this section object",
* @param none",
* @return String URL fragment, beginning with '&'"
*/
module.exports.define("getReferURLParams", function () {
    return "&refer_page=" + this.owner.page.id + "&refer_section=" + this.id;
});

module.exports.define("deduceKey", function () {
    var key;
    var link_field;

    if (this.key) {                         // key specified directly as a property
        key = this.key;
    } else if (this.link_field) {           // via 'link_field' property
        link_field = this.owner.page.getPrimaryRow().getField(this.link_field);
        if (!link_field) {
            this.throwError("link field invalid");
        }
        key = link_field.get();
    } else if (this.owner.page.page_key_entity) {       // having same entity as page_key_entity
        if (this.entity.id === this.owner.page.page_key_entity.id && this.owner.page.page_key) {
            key = this.owner.page.page_key;
        }
    // having same key as page
    } else if (this.entity.id === this.owner.page.entity.id && this.owner.page.page_key) {
        key = this.owner.page.page_key;
    }
    return key;
});

/**
* Create a new section object in the owning page, using the spec properties supplied
* @param Spec object whose properties will be given to the newly-created section
* @return Newly-created section object
*/
UI.Page.sections.override("add", function (spec) {
    var section;
    if (!spec.type) {
        this.throwError("Section type must be specified in spec: " + spec.id);
    }
    section = UI.sections.getThrowIfUnrecognized(spec.type).clone(spec);
    Core.OrderedMap.add.call(this, section);
    return section;
});

<|endoftext|>


<commit_before>var config = {
  allScriptsTimeout: 11000,

  capabilities: {
    browserName: 'chrome',
    shardTestFiles: true,
    maxInstances: 2
  },

  specs: [
    'tests/integration/**/*.js'
  ],

  seleniumAddress: 'http://localhost:4444/wd/hub',
  baseUrl: 'http://localhost:8000/examples/',

  framework: 'jasmine',
  rootElement: 'body',

  jasmineNodeOpts: {
    showColors: true, // Use colors in the command line report.
    defaultTimeoutInterval: 30000
  }
};

if (process.env.TRAVIS_BUILD_NUMBER) {
  config.sauceUser = process.env.SAUCE_USERNAME;
  config.sauceKey = process.env.SAUCE_ACCESS_KEY;
  config.capabilities = {
    'browserName': 'chrome',
    'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
    'build': process.env.TRAVIS_BUILD_NUMBER,
    'name': 'Tests'
  };
}

exports.config = config;<commit_msg>Tweaks to protractor conf<commit_after>var config = {
  allScriptsTimeout: 11000,

  capabilities: {
    browserName: 'chrome',
    shardTestFiles: true,
    maxInstances: 2
  },

  specs: [
    'tests/integration/**/*.js'
  ],

  seleniumAddress: 'http://localhost:4444/wd/hub',
  baseUrl: 'http://localhost:8000/examples/',

  framework: 'jasmine',
  rootElement: 'body',

  jasmineNodeOpts: {
    showColors: true, // Use colors in the command line report.
    defaultTimeoutInterval: 30000
  }
};

if (process.env.TRAVIS_BUILD_NUMBER) {
  config.sauceUser = process.env.SAUCE_USERNAME;
  config.sauceKey = process.env.SAUCE_ACCESS_KEY;
  config.capabilities['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
  config.capabilities.build = process.env.TRAVIS_BUILD_NUMBER;
}

exports.config = config;<|endoftext|>


<commit_before>/* eslint-env node */
"use strict";

module.exports = {
    name: "a24-ember-test-lib",
    included: function(app) {
        this._super.included.apply(this, arguments);

        //Testing helpers
        app.import({test: "vendor/a24testLib/testCaseHelpersExtend.js"});
    }
    // This needs to be removed (or be true, but since I cant commit that way I leave it commented out) for ESLint
    // to also run for actual file during test, else it will only run lint for the test files.
    // ,isDevelopingAddon: function() {
    //     return true;
    // }
};
<commit_msg>Updated isDevelopingAddon to be true during test ENV<commit_after>/* eslint-env node */
"use strict";

module.exports = {
    name: "a24-ember-test-lib",
    included: function(app) {
        this._super.included.apply(this, arguments);

        //Testing helpers
        app.import({test: "vendor/a24testLib/testCaseHelpersExtend.js"});
    },

    // For eslint to run during test we need isDevelopingAddon to return true. So below it will return true when
    // the ENV is test AND when the module running matches the addon. So test in parent app will NOT run eslint in this
    // addon, thus if an addon has failing eslint test it will not break the parent apps test.
    isDevelopingAddon: function() {
        if (process.env.EMBER_ENV === "test" && this.app && this.moduleName() === this.app.project.name()) {
            return true;
        } else {
            return false; //For devs, toggle this flag to true to enable dev mode during serve
        }
    }
};
<|endoftext|>


<commit_before>/*    Copyright 2016 - 2020 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program 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 Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const extensionManager = require('./ExtensionManager.js')

const f = require('../net2/Firewalla.js');

const userConfigFolder = f.getUserConfigFolder();
const dnsmasqConfigFolder = `${userConfigFolder}/dnsmasq`;

const configKey = "ext.safeSearch.config";

const rclient = require('../util/redis_manager.js').getRedisClient();

const domainBlock = require('../control/DomainBlock.js');

const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();
const featureName = "safe_search";
const policyKeyName = "safeSearch";

const NetworkProfileManager = require('../net2/NetworkProfileManager.js');
const NetworkProfile = require('../net2/NetworkProfile.js');
const TagManager = require('../net2/TagManager.js');

const iptool = require('ip')

class SafeSearchPlugin extends Sensor {

  async run() {

    this.systemSwitch = false;
    this.adminSystemSwitch = false;
    this.macAddressSettings = {};
    this.networkSettings = {};
    this.tagSettings = {};

    this.domainCaches = {};

    const exists = await this.configExists();

    if(!exists) {
      await this.setDefaultSafeSearchConfig();
    }

    extensionManager.registerExtension(policyKeyName, this, {
      applyPolicy: this.applyPolicy,
      start: this.start,
      stop: this.stop
    });

    this.hookFeature(featureName);

    sem.on('SAFESEARCH_REFRESH', (event) => {
      this.applySafeSearch();
    });
  }

  async job() {
    await this.updateAllDomains();
    await this.applySafeSearch();
  }

  async apiRun() {
    extensionManager.onSet("safeSearchConfig", async (msg, data) => {
      await rclient.setAsync(configKey, JSON.stringify(data));
      sem.sendEventToFireMain({
        type: 'SAFESEARCH_REFRESH'
      });
    });

    extensionManager.onGet("safeSearchConfig", async (msg, data) => {
      return this.getSafeSearchConfig();
    });
  }

  async getSafeSearchConfig() {
    const json = await rclient.getAsync(configKey);
    try {
      return JSON.parse(json);
    } catch(err) {
      log.error(`Got error when loading config from ${configKey}`);
      return {};
    }
  }

  async setDefaultSafeSearchConfig() {
    if(this.config && this.config.defaultConfig) {
      log.info("Setting default safe search config...");
      return rclient.setAsync(configKey, JSON.stringify(this.config.defaultConfig));
    }
  }

  async configExists() {
    const check = await rclient.typeAsync(configKey);
    return check !== 'none';
  }

  async applyPolicy(host, ip, policy) {
    log.info("Applying safesearch policy:", ip, policy)

    try {
      if(ip === '0.0.0.0') {
        if(policy && policy.state) {
          this.systemSwitch = true;
        } else {
          this.systemSwitch = false;
        }
        return this.applySystemSafeSearch();
      } else {
        if (!host)
          return;
        switch (host.constructor.name) {
          case "Tag": {
            const tagUid = host.o && host.o.uid;
            if (tagUid) {
              if (policy && policy.state === true)
                this.tagSettings[tagUid] = 1;
              // false means unset, this is for backward compatibility
              if (policy && policy.state === false)
                this.tagSettings[tagUid] = 0;
              // null means disabled, this is for backward compatibility
              if (policy && policy.state === null)
                this.tagSettings[tagUid] = -1;
              await this.applyTagSafeSearch(tagUid);
            }
            break;
          }
          case "NetworkProfile": {
            const uuid = host.o && host.o.uuid;
            if (uuid) {
              if (policy && policy.state === true)
                this.networkSettings[uuid] = 1;
              if (policy && policy.state === false)
                this.networkSettings[uuid] = 0;
              if (policy && policy.state === null)
                this.networkSettings[uuid] = -1;
              await this.applyNetworkSafeSearch(uuid);
            }
            break;
          }
          case "Host" : {
            const macAddress = host && host.o && host.o.mac;
            if (macAddress) {
              if (policy && policy.state === true)
                this.macAddressSettings[macAddress] = 1;
              if (policy && policy.state === false)
                this.macAddressSettings[macAddress] = 0;
              if (policy && policy.state === null)
                this.macAddressSettings[macAddress] = -1;
              await this.applyDeviceSafeSearch(macAddress);
            }
            break;
          }
          default:
        }
      }
    } catch(err) {
      log.error("Got error when applying safesearch policy", err);
    }
  }

  // {
  //   google: "on", // "on", "off"
  //   youtube: "strict", // "strict", "moderate", "off"
  //   bing: "on" // "on", "off"
  // }

  getMappingKey(type, value) {
    switch(type) {
    case "youtube":
      switch(value) {
        case "on":
        case "strict":
          return "youtube_strict";
        case "moderate":
          return "youtube_moderate";
        default:
          return null;
      }
    default:
      return type;
    }
  }

  getMappingResult(key) {
    if(key) {
      const mapping = this.getDomainMapping();
      if(!mapping) {
        return null;
      }
      return mapping[key];
    } else {
      return null;
    }
  }

  getDomainMapping() {
    return this.config && this.config.mapping;
  }

  getDNSMasqEntry(ipAddress, domainToBeRedirect) {
    return `address=/${domainToBeRedirect}/${ipAddress}$${featureName}`;
  }

  async loadDomainCache(domain) {
    const key = `rdns:domain:${domain}`;
    let results = await rclient.zrevrangebyscoreAsync(key, '+inf', '-inf');
    results = results.filter((ip) => !f.isReservedBlockingIP(ip));

    const ipv4Results = results.filter((ip) => iptool.isV4Format(ip))

    if(ipv4Results.length > 0) {
      return ipv4Results[0]; // return ipv4 address as a priority
    }

    if(results.length > 0) {
      log.info(`Domain ${domain} ======> ${results[0]}`);
      return results[0];
    }

    return null;
  }

  async updateDomainCache(domain) {
    return domainBlock.resolveDomain(domain);
  }

  getAllDomains() {
    const mappings = this.getDomainMapping();
    if(mappings) {
      const values = Object.values(mappings);
      const domains = [];
      values.forEach((value) => {
        if(value) {
          domains.push(...Object.keys(value));
        }
      });
      return domains;
    } else {
      return [];
    }
  }

  async updateAllDomains() {
    log.info("Updating all domains...");
    return Promise.all(this.getAllDomains().map(async domain => this.updateDomainCache(domain)));
  }

  // redirect targetDomain to the ip address of safe domain
  async generateDomainEntries(safeDomain, targetDomains) {
    const ip = await this.loadDomainCache(safeDomain);
    if(ip) {
      return targetDomains.map((targetDomain) => {
        return this.getDNSMasqEntry(ip, targetDomain);
      })
    } else {
      return [];
    }
  }

  async applySafeSearch() {
    const configFilePath = `${dnsmasqConfigFolder}/${featureName}.conf`;
    if (this.adminSystemSwitch) {
      const config = await this.getSafeSearchConfig();
      const entries = await this.generateDnsmasqEntries(config);
      await fs.writeFileAsync(configFilePath, entries);
    } else {
      await fs.unlinkAsync(configFilePath).catch((err) => {});
    }

    await this.applySystemSafeSearch();
    for(const macAddress in this.macAddressSettings) {
      await this.applyDeviceSafeSearch(macAddress);
    }
    for (const tagUid in this.tagSettings) {
      const tag = TagManager.getTagByUid(tagUid);
      if (!tag)
        // reset tag if it is already deleted
        this.tagSettings[tagUid] = 0;
      await this.applyTagSafeSearch(tagUid);
      if (!tag)
        delete this.tagSettings[tagUid];
    }
    for (const uuid in this.networkSettings) {
      const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
      if (!networkProfile)
        delete this.networkSettings[uuid];
      else
        await this.applyNetworkSafeSearch(uuid);
    }
  }

  async applySystemSafeSearch() {
    if(this.systemSwitch) {
      return this.systemStart();
    } else {
      return this.systemStop();
    }
  }

  async applyTagSafeSearch(tagUid) {
    if (this.tagSettings[tagUid] == 1)
      return this.perTagStart(tagUid);
    if (this.tagSettings[tagUid] == -1)
      return this.perTagStop(tagUid);
    return this.perTagReset(tagUid);
  }

  async applyNetworkSafeSearch(uuid) {
    if (this.networkSettings[uuid] == 1)
      return this.perNetworkStart(uuid);
    if (this.networkSettings[uuid] == -1)
      return this.perNetworkStop(uuid);
    return this.perNetworkReset(uuid);
  }

  async applyDeviceSafeSearch(macAddress) {
    if (this.macAddressSettings[macAddress] == 1)
      return this.perDeviceStart(macAddress);
    if (this.macAddressSettings[macAddress] == -1)
      return this.perDeviceStop(macAddress);
    return this.perDeviceReset(macAddress);
  }

  async generateDnsmasqEntries(config) {
    let entries = [];

    for(const type in config) {
      const value = config[type];
      if(value === 'off') {
        continue;
      }

      const key = this.getMappingKey(type, value);
      const result = this.getMappingResult(key);
      if(result) {
        const safeDomains = Object.keys(result);
        await Promise.all(safeDomains.map(async (safeDomain) => {
          const targetDomains = result[safeDomain];
          const configs = await this.generateDomainEntries(safeDomain, targetDomains);
          entries.push(...configs);
        }));
      }
    }

    entries.push("");
    return entries.join("\n");
  }

  async systemStart() {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_system.conf`;
    const dnsmasqEntry = `mac-address-tag=%FF:FF:FF:FF:FF:FF$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async systemStop() {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_system.conf`;
    const dnsmasqEntry = `mac-address-tag=%FF:FF:FF:FF:FF:FF$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagStart(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    const dnsmasqEntry = `group-tag=@${tagUid}$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagStop(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    const dnsmasqEntry = `group-tag=@${tagUid}$!${featureName}\n`; // match negative tag
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagReset(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkStart(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    const dnsmasqEntry = `mac-address-tag=%00:00:00:00:00:00$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkStop(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    // explicit disable family protect
    const dnsmasqEntry = `mac-address-tag=%00:00:00:00:00:00$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkReset(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    // remove config file
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceStart(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    const dnsmasqentry = `mac-address-tag=%${macAddress.toUpperCase()}$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqentry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceStop(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    const dnsmasqentry = `mac-address-tag=%${macAddress.toUpperCase()}$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqentry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceReset(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    // remove config file
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  // global on/off
  async globalOn() {
    this.adminSystemSwitch = true;
    await this.applySafeSearch();
  }

  async globalOff() {
    this.adminSystemSwitch = false;
    await this.applySafeSearch();
  }
}

module.exports = SafeSearchPlugin
<commit_msg>update domain mapping in SafeSearchPlugin.globalOn<commit_after>/*    Copyright 2016 - 2020 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program 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 Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const extensionManager = require('./ExtensionManager.js')

const f = require('../net2/Firewalla.js');

const userConfigFolder = f.getUserConfigFolder();
const dnsmasqConfigFolder = `${userConfigFolder}/dnsmasq`;

const configKey = "ext.safeSearch.config";

const rclient = require('../util/redis_manager.js').getRedisClient();

const domainBlock = require('../control/DomainBlock.js');

const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();
const featureName = "safe_search";
const policyKeyName = "safeSearch";

const NetworkProfileManager = require('../net2/NetworkProfileManager.js');
const NetworkProfile = require('../net2/NetworkProfile.js');
const TagManager = require('../net2/TagManager.js');

const iptool = require('ip')

class SafeSearchPlugin extends Sensor {

  async run() {

    this.systemSwitch = false;
    this.adminSystemSwitch = false;
    this.macAddressSettings = {};
    this.networkSettings = {};
    this.tagSettings = {};

    this.domainCaches = {};

    const exists = await this.configExists();

    if(!exists) {
      await this.setDefaultSafeSearchConfig();
    }

    extensionManager.registerExtension(policyKeyName, this, {
      applyPolicy: this.applyPolicy,
      start: this.start,
      stop: this.stop
    });

    this.hookFeature(featureName);

    sem.on('SAFESEARCH_REFRESH', (event) => {
      this.applySafeSearch();
    });
  }

  async job() {
    await this.updateAllDomains();
    await this.applySafeSearch();
  }

  async apiRun() {
    extensionManager.onSet("safeSearchConfig", async (msg, data) => {
      await rclient.setAsync(configKey, JSON.stringify(data));
      sem.sendEventToFireMain({
        type: 'SAFESEARCH_REFRESH'
      });
    });

    extensionManager.onGet("safeSearchConfig", async (msg, data) => {
      return this.getSafeSearchConfig();
    });
  }

  async getSafeSearchConfig() {
    const json = await rclient.getAsync(configKey);
    try {
      return JSON.parse(json);
    } catch(err) {
      log.error(`Got error when loading config from ${configKey}`);
      return {};
    }
  }

  async setDefaultSafeSearchConfig() {
    if(this.config && this.config.defaultConfig) {
      log.info("Setting default safe search config...");
      return rclient.setAsync(configKey, JSON.stringify(this.config.defaultConfig));
    }
  }

  async configExists() {
    const check = await rclient.typeAsync(configKey);
    return check !== 'none';
  }

  async applyPolicy(host, ip, policy) {
    log.info("Applying safesearch policy:", ip, policy)

    try {
      if(ip === '0.0.0.0') {
        if(policy && policy.state) {
          this.systemSwitch = true;
        } else {
          this.systemSwitch = false;
        }
        return this.applySystemSafeSearch();
      } else {
        if (!host)
          return;
        switch (host.constructor.name) {
          case "Tag": {
            const tagUid = host.o && host.o.uid;
            if (tagUid) {
              if (policy && policy.state === true)
                this.tagSettings[tagUid] = 1;
              // false means unset, this is for backward compatibility
              if (policy && policy.state === false)
                this.tagSettings[tagUid] = 0;
              // null means disabled, this is for backward compatibility
              if (policy && policy.state === null)
                this.tagSettings[tagUid] = -1;
              await this.applyTagSafeSearch(tagUid);
            }
            break;
          }
          case "NetworkProfile": {
            const uuid = host.o && host.o.uuid;
            if (uuid) {
              if (policy && policy.state === true)
                this.networkSettings[uuid] = 1;
              if (policy && policy.state === false)
                this.networkSettings[uuid] = 0;
              if (policy && policy.state === null)
                this.networkSettings[uuid] = -1;
              await this.applyNetworkSafeSearch(uuid);
            }
            break;
          }
          case "Host" : {
            const macAddress = host && host.o && host.o.mac;
            if (macAddress) {
              if (policy && policy.state === true)
                this.macAddressSettings[macAddress] = 1;
              if (policy && policy.state === false)
                this.macAddressSettings[macAddress] = 0;
              if (policy && policy.state === null)
                this.macAddressSettings[macAddress] = -1;
              await this.applyDeviceSafeSearch(macAddress);
            }
            break;
          }
          default:
        }
      }
    } catch(err) {
      log.error("Got error when applying safesearch policy", err);
    }
  }

  // {
  //   google: "on", // "on", "off"
  //   youtube: "strict", // "strict", "moderate", "off"
  //   bing: "on" // "on", "off"
  // }

  getMappingKey(type, value) {
    switch(type) {
    case "youtube":
      switch(value) {
        case "on":
        case "strict":
          return "youtube_strict";
        case "moderate":
          return "youtube_moderate";
        default:
          return null;
      }
    default:
      return type;
    }
  }

  getMappingResult(key) {
    if(key) {
      const mapping = this.getDomainMapping();
      if(!mapping) {
        return null;
      }
      return mapping[key];
    } else {
      return null;
    }
  }

  getDomainMapping() {
    return this.config && this.config.mapping;
  }

  getDNSMasqEntry(ipAddress, domainToBeRedirect) {
    return `address=/${domainToBeRedirect}/${ipAddress}$${featureName}`;
  }

  async loadDomainCache(domain) {
    const key = `rdns:domain:${domain}`;
    let results = await rclient.zrevrangebyscoreAsync(key, '+inf', '-inf');
    results = results.filter((ip) => !f.isReservedBlockingIP(ip));

    const ipv4Results = results.filter((ip) => iptool.isV4Format(ip))

    if(ipv4Results.length > 0) {
      return ipv4Results[0]; // return ipv4 address as a priority
    }

    if(results.length > 0) {
      log.info(`Domain ${domain} ======> ${results[0]}`);
      return results[0];
    }

    return null;
  }

  async updateDomainCache(domain) {
    return domainBlock.resolveDomain(domain);
  }

  getAllDomains() {
    const mappings = this.getDomainMapping();
    if(mappings) {
      const values = Object.values(mappings);
      const domains = [];
      values.forEach((value) => {
        if(value) {
          domains.push(...Object.keys(value));
        }
      });
      return domains;
    } else {
      return [];
    }
  }

  async updateAllDomains() {
    log.info("Updating all domains...");
    return Promise.all(this.getAllDomains().map(async domain => this.updateDomainCache(domain)));
  }

  // redirect targetDomain to the ip address of safe domain
  async generateDomainEntries(safeDomain, targetDomains) {
    const ip = await this.loadDomainCache(safeDomain);
    if(ip) {
      return targetDomains.map((targetDomain) => {
        return this.getDNSMasqEntry(ip, targetDomain);
      })
    } else {
      return [];
    }
  }

  async applySafeSearch() {
    const configFilePath = `${dnsmasqConfigFolder}/${featureName}.conf`;
    if (this.adminSystemSwitch) {
      const config = await this.getSafeSearchConfig();
      const entries = await this.generateDnsmasqEntries(config);
      await fs.writeFileAsync(configFilePath, entries);
    } else {
      await fs.unlinkAsync(configFilePath).catch((err) => {});
    }

    await this.applySystemSafeSearch();
    for(const macAddress in this.macAddressSettings) {
      await this.applyDeviceSafeSearch(macAddress);
    }
    for (const tagUid in this.tagSettings) {
      const tag = TagManager.getTagByUid(tagUid);
      if (!tag)
        // reset tag if it is already deleted
        this.tagSettings[tagUid] = 0;
      await this.applyTagSafeSearch(tagUid);
      if (!tag)
        delete this.tagSettings[tagUid];
    }
    for (const uuid in this.networkSettings) {
      const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
      if (!networkProfile)
        delete this.networkSettings[uuid];
      else
        await this.applyNetworkSafeSearch(uuid);
    }
  }

  async applySystemSafeSearch() {
    if(this.systemSwitch) {
      return this.systemStart();
    } else {
      return this.systemStop();
    }
  }

  async applyTagSafeSearch(tagUid) {
    if (this.tagSettings[tagUid] == 1)
      return this.perTagStart(tagUid);
    if (this.tagSettings[tagUid] == -1)
      return this.perTagStop(tagUid);
    return this.perTagReset(tagUid);
  }

  async applyNetworkSafeSearch(uuid) {
    if (this.networkSettings[uuid] == 1)
      return this.perNetworkStart(uuid);
    if (this.networkSettings[uuid] == -1)
      return this.perNetworkStop(uuid);
    return this.perNetworkReset(uuid);
  }

  async applyDeviceSafeSearch(macAddress) {
    if (this.macAddressSettings[macAddress] == 1)
      return this.perDeviceStart(macAddress);
    if (this.macAddressSettings[macAddress] == -1)
      return this.perDeviceStop(macAddress);
    return this.perDeviceReset(macAddress);
  }

  async generateDnsmasqEntries(config) {
    let entries = [];

    for(const type in config) {
      const value = config[type];
      if(value === 'off') {
        continue;
      }

      const key = this.getMappingKey(type, value);
      const result = this.getMappingResult(key);
      if(result) {
        const safeDomains = Object.keys(result);
        await Promise.all(safeDomains.map(async (safeDomain) => {
          const targetDomains = result[safeDomain];
          const configs = await this.generateDomainEntries(safeDomain, targetDomains);
          entries.push(...configs);
        }));
      }
    }

    entries.push("");
    return entries.join("\n");
  }

  async systemStart() {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_system.conf`;
    const dnsmasqEntry = `mac-address-tag=%FF:FF:FF:FF:FF:FF$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async systemStop() {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_system.conf`;
    const dnsmasqEntry = `mac-address-tag=%FF:FF:FF:FF:FF:FF$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagStart(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    const dnsmasqEntry = `group-tag=@${tagUid}$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagStop(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    const dnsmasqEntry = `group-tag=@${tagUid}$!${featureName}\n`; // match negative tag
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perTagReset(tagUid) {
    const configFile = `${dnsmasqConfigFolder}/tag_${tagUid}_${featureName}.conf`;
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkStart(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    const dnsmasqEntry = `mac-address-tag=%00:00:00:00:00:00$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkStop(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    // explicit disable family protect
    const dnsmasqEntry = `mac-address-tag=%00:00:00:00:00:00$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqEntry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perNetworkReset(uuid) {
    const networkProfile = NetworkProfileManager.getNetworkProfile(uuid);
    const iface = networkProfile && networkProfile.o && networkProfile.o.intf;
    if (!iface) {
      log.warn(`Interface name is not found on ${uuid}`);
      return;
    }
    const configFile = `${NetworkProfile.getDnsmasqConfigDirectory(uuid)}/${featureName}_${iface}.conf`;
    // remove config file
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceStart(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    const dnsmasqentry = `mac-address-tag=%${macAddress.toUpperCase()}$${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqentry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceStop(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    const dnsmasqentry = `mac-address-tag=%${macAddress.toUpperCase()}$!${featureName}\n`;
    await fs.writeFileAsync(configFile, dnsmasqentry);
    dnsmasq.scheduleRestartDNSService();
  }

  async perDeviceReset(macAddress) {
    const configFile = `${dnsmasqConfigFolder}/${featureName}_${macAddress}.conf`;
    // remove config file
    await fs.unlinkAsync(configFile).catch((err) => {});
    dnsmasq.scheduleRestartDNSService();
  }

  // global on/off
  async globalOn() {
    this.adminSystemSwitch = true;
    await this.updateAllDomains();
    await this.applySafeSearch();
  }

  async globalOff() {
    this.adminSystemSwitch = false;
    await this.applySafeSearch();
  }
}

module.exports = SafeSearchPlugin
<|endoftext|>


<commit_before>//@ts-check
const fs = require("fs");
const https = require('https');
const tls = require("tls");
/*

To enable HTTPS, add "https": "./https.js" to the settings.json > bindInfo object.
The path is relative to settings.json, so change it if necessary.

I use acme.sh for managing my certificates https://github.com/Neilpang/acme.sh

This is my issuecert.sh file (The key is RSA 4096)

/root/.acme.sh/acme.sh --issue --standalone -k 4096 -d example.com

This is my installcert.sh file

/root/.acme.sh/acme.sh --install-cert -d icedteadrinker.sandsendhand.net \
--cert-file      /root/TiddlyServer/tiddlyserver-cert.pem  \
--key-file       /root/TiddlyServer/tiddlyserver-key.pem  \
--fullchain-file /root/TiddlyServer/tiddlyserver-fullchain.pem \
--reloadcmd     "pm2 restart 7" << change this to whatever your restart server command is

You should run these as the same user that your restart server command runs under, not as sudo

Change the paths to wherever you have your certificates stored.

*/

module.exports.serverOptions = function serverOptions (iface) {
	/** @type { import("tls").TlsOptions } Server options object */
	const res = {
    //use require.resolve if you want a file relative to this file
		key: [fs.readFileSync("tiddlyserver.key")], // server key file
		cert: [fs.readFileSync("tiddlyserver.cer")], //server certificate
    // pfx: [fs.readFileSync("server.pfx")], //server pfx (key and cert)

		//passphrase for password protected server key and pfx files
		// passphrase: "",
	};
	return res;
}<commit_msg>Update https.js<commit_after>//@ts-check
const fs = require("fs");
const https = require('https');
const tls = require("tls");
/*

To enable HTTPS, add "https": "./https.js" to the settings.json > bindInfo object.
The path is relative to settings.json, so change it if necessary.

I use acme.sh for managing my certificates https://github.com/Neilpang/acme.sh

This is my issuecert.sh file (The key is RSA 4096)

/root/.acme.sh/acme.sh --issue --standalone -k 4096 -d example.com

This is my installcert.sh file

/root/.acme.sh/acme.sh --install-cert -d example.com \
--cert-file      /root/TiddlyServer/tiddlyserver-cert.pem  \
--key-file       /root/TiddlyServer/tiddlyserver-key.pem  \
--fullchain-file /root/TiddlyServer/tiddlyserver-fullchain.pem \
--reloadcmd     "pm2 restart 7" << change this to whatever your restart server command is

You should run these as the same user that your restart server command runs under, not as sudo

Change the paths to wherever you have your certificates stored.

*/

module.exports.serverOptions = function serverOptions (iface) {
	/** @type { import("tls").TlsOptions } Server options object */
	const res = {
    //use require.resolve if you want a file relative to this file
		key: [fs.readFileSync("tiddlyserver.key")], // server key file
		cert: [fs.readFileSync("tiddlyserver.cer")], //server certificate
    // pfx: [fs.readFileSync("server.pfx")], //server pfx (key and cert)

		//passphrase for password protected server key and pfx files
		// passphrase: "",
	};
	return res;
}<|endoftext|>


<commit_before>"use strict";
var px = require("../px");
var gfx = require("../gfx");
var ui = require("../uiutil");
var chat = require("../chat");
var mkAi = require("../mkAi");
var sock = require("../sock");
var etgutil = require("../etgutil");
var options = require("../options");
var userutil = require("../userutil");
module.exports = function(game) {
	var winner = game.winner == game.player1, stage;
	function exitFunc(){
		if (game.quest) {
			if (winner && game.choicerewards)
				require("./Reward")(game.choicerewards, game.rewardamount);
			else
				require("./QuestArea")(game.area);
		}
		else if (game.daily !== undefined){
			require("./Colosseum")();
		}else require("./MainMenu")();
	}
	function rematch(){
		switch(game.level){
		case 0:mkAi.mkAi(0)();break;
		case 1:mkAi.mkPremade("mage")();break;
		case 2:mkAi.mkAi(2)();break;
		case 3:mkAi.mkPremade("demigod")();break;
		case 4:sock.userEmit("foearena", {lv:0});break;
		case 5:sock.userEmit("foearena", {lv:1});break;
		default:
			if (game.foename == "Custom"){
				var gameData = { deck: options.aideck, urdeck: sock.getDeck(), seed: Math.random() * etgutil.MAX_INT, foename: "Custom", cardreward: "" };
				ui.parsepvpstats(gameData);
				ui.parseaistats(gameData);
				require("./Match")(gameData, true);
			}
		}
	}
	var dom = [
		[10, 290, game.ply + " plies\n" + (game.time / 1000).toFixed(1) + " seconds\n" + (winner && sock.user && game.level !== undefined ? (sock.user["streak" + game.level] || 0) + " win streak\n+" +
			Math.min([5, 5, 7.5, 10, 7.5, 10][game.level] * Math.max(sock.user["streak" + game.level] - 1, 0), 100) + "% streak bonus" : "")],
		[412, 440, ["Exit", exitFunc]]
	];

	if (!game.quest && game.daily === undefined){
		dom.push([412, 490, ["Rematch", rematch]]);
	}

	if (winner){
		var tinfo = px.domText(game.quest ? game.wintext : "You won!");
		tinfo.style.textAlign = "center";
		tinfo.style.width = "900px";
		dom.push([0, game.cardreward ? 130 : 250, tinfo]);
	}

	if (winner && sock.user){
		if (game.level !== undefined || !game.ai) sock.userExec("addwin", { pvp: !game.ai });
		if (game.goldreward) {
			var goldwon = px.domText((game.goldreward - (game.cost || 0)) + "$");
			goldwon.style.textAlign = "center";
			goldwon.style.width = "900px";
			dom.push([0, 550, goldwon]);
			sock.userExec("addgold", { g: game.goldreward });
		}
		if (game.cardreward) {
			var x0 = 470-etgutil.decklength(game.cardreward)*20;
			stage = new PIXI.Container();
			etgutil.iterdeck(game.cardreward, function(code, i){
				var cardArt = new PIXI.Sprite(gfx.getArt(code));
				cardArt.anchor.x = .5;
				cardArt.position.set(x0+i*40, 170);
				stage.addChild(cardArt);
			});
			sock.userExec(game.quest?"addbound":"addcards", { c: game.cardreward });
		}
	}
	if (options.stats){
		chat([game.level === undefined ? -1 : game.level,
			(game.foename || "?").replace(/,/g, " "),
			winner ? "W" : "L",
			game.ply,
			game.time,
			game.player1.hp,
			game.player1.maxhp,
			(game.goldreward || 0) - (game.cost || 0),
			game.cardreward || "-",
			userutil.calcWealth(etgutil.deck2pool(game.cardreward)),
			!sock.user || game.level === undefined ? -1 : sock.user["streak"+game.level],
			!sock.user || game.level === undefined ? 0 : Math.min([.05, .05, .075, .1, .075, .1][game.level]*Math.max(sock.user["streak"+game.level]-1, 0), 1).toFixed(3).replace(/\.?0+$/, "")].join());
	}
	function onkeydown(e){
		if (e.keyCode == 32) exitFunc();
		else if (e.keyCode == 87 && !game.quest && game.daily === undefined){
			rematch();
		}
	}
	document.addEventListener("keydown", onkeydown);

	px.refreshRenderer({view:stage, domvic:dom, endnext: function() {
		document.removeEventListener("keydown", onkeydown);
	}});
}<commit_msg>Lift gamewon text for long winded quest won text<commit_after>"use strict";
var px = require("../px");
var gfx = require("../gfx");
var ui = require("../uiutil");
var chat = require("../chat");
var mkAi = require("../mkAi");
var sock = require("../sock");
var etgutil = require("../etgutil");
var options = require("../options");
var userutil = require("../userutil");
module.exports = function(game) {
	var winner = game.winner == game.player1, stage;
	function exitFunc(){
		if (game.quest) {
			if (winner && game.choicerewards)
				require("./Reward")(game.choicerewards, game.rewardamount);
			else
				require("./QuestArea")(game.area);
		}
		else if (game.daily !== undefined){
			require("./Colosseum")();
		}else require("./MainMenu")();
	}
	function rematch(){
		switch(game.level){
		case 0:mkAi.mkAi(0)();break;
		case 1:mkAi.mkPremade("mage")();break;
		case 2:mkAi.mkAi(2)();break;
		case 3:mkAi.mkPremade("demigod")();break;
		case 4:sock.userEmit("foearena", {lv:0});break;
		case 5:sock.userEmit("foearena", {lv:1});break;
		default:
			if (game.foename == "Custom"){
				var gameData = { deck: options.aideck, urdeck: sock.getDeck(), seed: Math.random() * etgutil.MAX_INT, foename: "Custom", cardreward: "" };
				ui.parsepvpstats(gameData);
				ui.parseaistats(gameData);
				require("./Match")(gameData, true);
			}
		}
	}
	var dom = [
		[10, 290, game.ply + " plies\n" + (game.time / 1000).toFixed(1) + " seconds\n" + (winner && sock.user && game.level !== undefined ? (sock.user["streak" + game.level] || 0) + " win streak\n+" +
			Math.min([5, 5, 7.5, 10, 7.5, 10][game.level] * Math.max(sock.user["streak" + game.level] - 1, 0), 100) + "% streak bonus" : "")],
		[412, 440, ["Exit", exitFunc]]
	];

	if (!game.quest && game.daily === undefined){
		dom.push([412, 490, ["Rematch", rematch]]);
	}

	if (winner){
		var tinfo = px.domText(game.quest ? game.wintext : "You won!");
		tinfo.style.textAlign = "center";
		tinfo.style.width = "900px";
		dom.push([0, game.cardreward ? 100 : 250, tinfo]);
	}

	if (winner && sock.user){
		if (game.level !== undefined || !game.ai) sock.userExec("addwin", { pvp: !game.ai });
		if (game.goldreward) {
			var goldwon = px.domText((game.goldreward - (game.cost || 0)) + "$");
			goldwon.style.textAlign = "center";
			goldwon.style.width = "900px";
			dom.push([0, 550, goldwon]);
			sock.userExec("addgold", { g: game.goldreward });
		}
		if (game.cardreward) {
			var x0 = 470-etgutil.decklength(game.cardreward)*20;
			stage = new PIXI.Container();
			etgutil.iterdeck(game.cardreward, function(code, i){
				var cardArt = new PIXI.Sprite(gfx.getArt(code));
				cardArt.anchor.x = .5;
				cardArt.position.set(x0+i*40, 170);
				stage.addChild(cardArt);
			});
			sock.userExec(game.quest?"addbound":"addcards", { c: game.cardreward });
		}
	}
	if (options.stats){
		chat([game.level === undefined ? -1 : game.level,
			(game.foename || "?").replace(/,/g, " "),
			winner ? "W" : "L",
			game.ply,
			game.time,
			game.player1.hp,
			game.player1.maxhp,
			(game.goldreward || 0) - (game.cost || 0),
			game.cardreward || "-",
			userutil.calcWealth(etgutil.deck2pool(game.cardreward)),
			!sock.user || game.level === undefined ? -1 : sock.user["streak"+game.level],
			!sock.user || game.level === undefined ? 0 : Math.min([.05, .05, .075, .1, .075, .1][game.level]*Math.max(sock.user["streak"+game.level]-1, 0), 1).toFixed(3).replace(/\.?0+$/, "")].join());
	}
	function onkeydown(e){
		if (e.keyCode == 32) exitFunc();
		else if (e.keyCode == 87 && !game.quest && game.daily === undefined){
			rematch();
		}
	}
	document.addEventListener("keydown", onkeydown);

	px.refreshRenderer({view:stage, domvic:dom, endnext: function() {
		document.removeEventListener("keydown", onkeydown);
	}});
}<|endoftext|>


<commit_before><commit_msg>Spanish update<commit_after><|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_t<commit_msg>Correcting style<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_ti<|endoftext|>