<commit_before>(function ($) {
    jQuery.fn.saiForm = function () {

        /* Select */

        var firstClick = [];
        var newSelect;
        $(".saiForm").find("select").each(function (index) {
            firstClick[index] = true;
            var select = $(this);

            select.before("<input type='hidden' name='select" + (index + 1) + "'/>");
            var inputSelect = select.prev();
            if ($(select).attr("required") != undefined) {
                inputSelect.addClass("required");
            }
            var options = select.find("option");

            inputSelect.wrap("

").wrap("

");

            if (inputSelect.attr("placeholder") != undefined) {
                inputSelect.after("
" + inputSelect.attr("placeholder") + "
");
            } else {
                inputSelect.after("

");
            }
            if (inputSelect.attr("required") != undefined) {
                inputSelect.addClass("required");
            }

            inputSelect.closest(".input_wrapper").after("

");

            for (i = 0; i < options.length; i++) {
                if ($(options[i]).attr("selected") != undefined) {
                    inputSelect.closest(".input_wrapper").next().append("
" + $(options[i]).text() + "
");
                    $(inputSelect).next().text($(options[i]).text());
                } else {
                    inputSelect.closest(".input_wrapper").next().append("
" + $(options[i]).text() + "
");
                }
            }

            inputSelect.next().data("index", index);
            newSelect = inputSelect.closest(".input_wrapper").next();
            $(select).remove();
        });
        $(".input_wrapper .layer").on("click", function (e) {
            var ind = $(this).data("index");
            var thisSelect = $(this).parent().next(".select");
            var ths = $(this);

            if (thisSelect.css('display') != 'block') {
                thisSelect.fadeIn();
                ths.addClass("active");
                firstClick[ind] = true;
                $(document).bind('click.select' + ind + '', function () {
                    if (!firstClick[ind]) {
                        thisSelect.fadeOut();
                        ths.removeClass("active");
                        $(document).unbind('click.select' + ind + '');
                    }
                    firstClick[ind] = false;
                });
            }
            e.preventDefault();
        });

        $(".saiForm .select li").on("click", function () {
            $(this).parent().fadeOut();
            $(this).parent().parent().find(".layer").removeClass("active");
            $(this).parent().prev().find("input").removeAttr("value");
            if (!$(this).hasClass("selected")) {
                $(this).parent().prev().find("input").val($(this).attr("data-value"));
            }
            $(this).parent().prev().find("p").text($(this).text());
            for (i = 0; i < firstClick.length; i++) {
                $(document).unbind('click.select' + i + '');
                firstClick[i] = false;
            }
        });

        function valid_select() {
            var inputs = $(".saiForm").find(".select_wrapper .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).next().addClass("false_required");
                    if (!$(inputs[i]).closest(".select_wrapper").next(".message_false").length) {
                        $(inputs[i]).closest(".select_wrapper").after("
Select sex
");
                    } else {
                        $(inputs[i]).closest(".select_wrapper").next().text("Select sex");
                    }
                    $(inputs[i]).closest(".select_wrapper").next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).next().removeClass("false_required");
                    $(inputs[i]).closest(".select_wrapper").next(".message_false").remove();
                }
            }
        }
        /* end */

        /* All input */

        $(".saiForm").find("input[required], select[required], textarea[required]").removeAttr("required").addClass("required");

        /* Input text */
        $(".saiForm").find("input[type='text'], textarea").addClass("input_text");

        function valid_text() {
            var inputs = $(".saiForm").find(".input_text.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter " + $(inputs[i]).attr("placeholder") + "
");
                    } else {
                        $(inputs[i]).next().text("Enter " + $(inputs[i]).attr("placeholder") + "");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
        }
        /* End */

        /* E-Mail */

        if ($(".saiForm").find("input[type='email']").length != false) {

            $(".saiForm").find("input[type='email']").each(function () {
                $(this).attr("type", "text").addClass("email");
            });

        }

        function valid_email() {
            var inputs = $(".saiForm").find(".email.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter Email
");
                    } else {
                        $(inputs[i]).next().text("Enter Email");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\w+\.?\-?\_?\w+@\w+\.\w{2,4}$/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Make sure you entered is correct, valid characters ' . ' , ' - ' , ' _ '
");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Number */
        if ($(".saiForm").find("input[type='number']").length != false) {
            $(".saiForm").find("input[type='number']").each(function () {
                $(this).attr("type", "text").attr("readonly", "").wrap("

");
                $(this).parent().prepend("");
                $(this).parent().append("");

            });
            $(".number").on("click", ".up", function () {
                var val = Number($(this).parent().find("input").val());
                if ($(this).parent().find("input").val() == false) {
                    $(this).parent().find("input").val(1);
                } else {
                    val++;
                    $(this).parent().find("input").val(val);
                }
            });
            $(".number").on("click", ".down", function () {
                var val = Number($(this).parent().find("input").val());
                if (val == false) {
                    $(this).parent().find("input").val(1);
                } else if (val > 1) {
                    val--;
                    $(this).parent().find("input").val(val);
                }
            });
            function valid_number() {
                var inputs = $(".saiForm").find(".number .required");
                var end = true;
                for (i = 0; i < inputs.length; i++) {
                    if ($(inputs[i]).val() == false) {
                        $(inputs[i]).addClass("false_required");
                        if (!$(inputs[i]).parent().next(".message_false").length) {
                            $(inputs[i]).parent().after("
Select " + $(inputs[i]).attr("placeholder") + "
");
                        } else {
                            $(inputs[i]).parent().next().text("Select " + $(inputs[i]).attr("placeholder") + "");
                        }
                        $(inputs[i]).parent().next(".message_false").fadeIn();
                        end = false;
                    } else {
                        $(inputs[i]).removeClass("false_required");
                        $(inputs[i]).parent().next(".message_false").remove();
                    }
                }
            }
        }
        /* End */

        /* Phone */

        if ($(".saiForm").find("input[type='tel']").length != false) {

            $(".saiForm").find("input[type='tel']").each(function () {
                $(this).attr("type", "text").addClass("phone");
            });
        }

        function valid_tel() {
            var inputs = $(".saiForm").find(".phone.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter phone number
");
                    } else {
                        $(inputs[i]).next().text("Enter phone number");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\+/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
At the beginning to be '+'
");
                    } else {
                        $(inputs[i]).next().text("At the beginning to be '+'");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\+[0-9\s()-]+$/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Available characters '0-9 ( ) - '
");
                    } else {
                        $(inputs[i]).next().text("Available characters '0-9 ( ) - ' ");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if ($(inputs[i]).val().match(/[0-9]/gi).length < 10) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
The minimum number of 10
");
                    } else {
                        $(inputs[i]).next().text("The minimum number of 10");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Radio */

        if ($(".saiForm").find("input[type='radio']").length != false) {

            $(".saiForm").find("input[type='radio']").each(function (index) {
                index = index + 1;

                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " radio" + index + "");
                } else {
                    $(this).attr("id", "radio" + index + "");
                }
                $(this).after("");

            });
        }

        function valid_radio() {
            var groups = $(".saiForm").find(".radio_group");
            var end = false;
            $(".saiForm").find(".radio_group").each(function () {
                var end = false;

                $(this).find(".required").each(function () {
                    if ($(this).is(":checked")) {
                        end = true;
                    }
                });

                if (!end) {
                    $(this).find(".required").each(function () {
                        $(this).parent().addClass("false_required");
                    });
                    if (!$(this).next(".message_false").length) {
                        $(this).after("
Checked
");
                    } else {
                        $(this).next().text("Checked");
                    }
                    $(this).next(".message_false").fadeIn();
                } else {
                    $(this).find(".radio").removeClass("false_required");
                    $(this).next(".message_false").remove();
                }
            });

            return end;
        }
        /* End */

        /* CheckBox */

        if ($(".saiForm").find("input[type='checkbox']").length != false) {

            $(".saiForm").find("input[type='checkbox']").each(function (index) {
                index = index + 1;
                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " checkbox" + index++ + "");
                } else {
                    $(this).attr("id", "checkbox" + index + "");
                }
                $(this).after("");

            });
        }

        function valid_checkbox() {
            var inputs = $(".saiForm").find(".checkbox .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if (!$(inputs[i]).is(":checked")) {
                    $(inputs[i]).parent().find("label").addClass("false_required");
                    if (!$(inputs[i]).parent().next(".message_false").length) {
                        $(inputs[i]).parent().after("
Checked
");
                    } else {
                        $(inputs[i]).parent().next().text("Checked");
                    }
                    $(inputs[i]).parent().next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).parent().find("label").removeClass("false_required");
                    $(inputs[i]).parent().next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* File */

        if ($(".saiForm").find("input[type='file']").length != false) {

            $(".saiForm").find("input[type='file']").each(function (index) {
                index = index + 1;
                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " file" + index++ + "");
                } else {
                    $(this).attr("id", "file" + index + "");
                }
                $(this).after("Browse...");
                $(this).parent().append("");
                $(this).on("change", function () {
                    $(this).parent().find("span").text($(this).val());
                });
            });
        }

        function valid_file() {
            var inputs = $(".saiForm").find(".file .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).parent().find("label").addClass("false_required");
                    if (!$(inputs[i]).parent().next(".message_false").length) {
                        $(inputs[i]).parent().after("
Select file
");
                    } else {
                        $(inputs[i]).parent().next().text("Select file");
                    }
                    $(inputs[i]).parent().next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).parent().find("label").removeClass("false_required");
                    $(inputs[i]).parent().next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Submit */
        $(".saiForm").on("submit", function () {
            var valid = true;
            valid_email();
            valid_tel();
            valid_select();
            valid_text();
            valid_number();
            valid_checkbox();
            valid_file();
            valid_radio();
            return false;
        });
        /* End */
    };
})(jQuery);<commit_msg>master<commit_after>(function ($) {
    jQuery.fn.saiForm = function () {

        /* Select */

        var firstClick = [];
        var newSelect;
        $(".saiForm").find("select").each(function (index) {
            firstClick[index] = true;
            var select = $(this);

            select.before("<input type='hidden' name='select" + (index + 1) + "'/>");
            var inputSelect = select.prev();
            if ($(select).attr("required") != undefined) {
                inputSelect.addClass("required");
            }
            var options = select.find("option");

            inputSelect.wrap("

").wrap("

");

            if (inputSelect.attr("placeholder") != undefined) {
                inputSelect.after("
" + inputSelect.attr("placeholder") + "
");
            } else {
                inputSelect.after("

");
            }
            if (inputSelect.attr("required") != undefined) {
                inputSelect.addClass("required");
            }

            inputSelect.closest(".input_wrapper").after("

");

            for (i = 0; i < options.length; i++) {
                if ($(options[i]).attr("selected") != undefined) {
                    inputSelect.closest(".input_wrapper").next().append("
" + $(options[i]).text() + "
");
                    $(inputSelect).next().text($(options[i]).text());
                } else {
                    inputSelect.closest(".input_wrapper").next().append("
" + $(options[i]).text() + "
");
                }
            }

            inputSelect.next().data("index", index);
            newSelect = inputSelect.closest(".input_wrapper").next();
            $(select).remove();
        });
        $(".input_wrapper .layer").on("click", function (e) {
            var ind = $(this).data("index");
            var thisSelect = $(this).parent().next(".select");
            var ths = $(this);

            if (thisSelect.css('display') != 'block') {
                thisSelect.fadeIn();
                ths.addClass("active");
                firstClick[ind] = true;
                $(document).bind('click.select' + ind + '', function () {
                    if (!firstClick[ind]) {
                        thisSelect.fadeOut();
                        ths.removeClass("active");
                        $(document).unbind('click.select' + ind + '');
                    }
                    firstClick[ind] = false;
                });
            }
            e.preventDefault();
        });

        $(".saiForm .select li").on("click", function () {
            $(this).parent().fadeOut();
            $(this).parent().parent().find(".layer").removeClass("active");
            $(this).parent().prev().find("input").removeAttr("value");
            if (!$(this).hasClass("selected")) {
                $(this).parent().prev().find("input").val($(this).attr("data-value"));
            }
            $(this).parent().prev().find("p").text($(this).text());
            for (i = 0; i < firstClick.length; i++) {
                $(document).unbind('click.select' + i + '');
                firstClick[i] = false;
            }
        });

        function valid_select() {
            var inputs = $(".saiForm").find(".select_wrapper .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).next().addClass("false_required");
                    if (!$(inputs[i]).closest(".select_wrapper").next(".message_false").length) {
                        $(inputs[i]).closest(".select_wrapper").after("
Select sex
");
                    } else {
                        $(inputs[i]).closest(".select_wrapper").next().text("Select sex");
                    }
                    $(inputs[i]).closest(".select_wrapper").next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).next().removeClass("false_required");
                    $(inputs[i]).closest(".select_wrapper").next(".message_false").remove();
                }
            }
        }
        /* end */

        /* All input */

        $(".saiForm").find("input[required], select[required], textarea[required]").removeAttr("required").addClass("required");

        /* Input text */
        $(".saiForm").find("input[type='text'], textarea").addClass("input_text");

        function valid_text() {
            var inputs = $(".saiForm").find(".input_text.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter " + $(inputs[i]).attr("placeholder") + "
");
                    } else {
                        $(inputs[i]).next().text("Enter " + $(inputs[i]).attr("placeholder") + "");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
        }
        /* End */

        /* E-Mail */

        if ($(".saiForm").find("input[type='email']").length != false) {

            $(".saiForm").find("input[type='email']").each(function () {
                $(this).attr("type", "text").addClass("email");
            });

        }

        function valid_email() {
            var inputs = $(".saiForm").find(".email.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter Email
");
                    } else {
                        $(inputs[i]).next().text("Enter Email");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\w+\.?\-?\_?\w+@\w+\.\w{2,4}$/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Make sure you entered is correct, valid characters ' . ' , ' - ' , ' _ '
");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Number */
        if ($(".saiForm").find("input[type='number']").length != false) {
            $(".saiForm").find("input[type='number']").each(function () {
                $(this).attr("type", "text").attr("readonly", "").wrap("

");
                $(this).parent().prepend("");
                $(this).parent().append("");

            });
            $(".number").on("click", ".up", function () {
                var val = Number($(this).parent().find("input").val());
                if ($(this).parent().find("input").val() == false) {
                    $(this).parent().find("input").val(1);
                } else {
                    val++;
                    $(this).parent().find("input").val(val);
                }
            });
            $(".number").on("click", ".down", function () {
                var val = Number($(this).parent().find("input").val());
                if (val == false) {
                    $(this).parent().find("input").val(1);
                } else if (val > 1) {
                    val--;
                    $(this).parent().find("input").val(val);
                }
            });
            function valid_number() {
                var inputs = $(".saiForm").find(".number .required");
                var end = true;
                for (i = 0; i < inputs.length; i++) {
                    if ($(inputs[i]).val() == false) {
                        $(inputs[i]).addClass("false_required");
                        if (!$(inputs[i]).parent().next(".message_false").length) {
                            $(inputs[i]).parent().after("
Select " + $(inputs[i]).attr("placeholder") + "
");
                        } else {
                            $(inputs[i]).parent().next().text("Select " + $(inputs[i]).attr("placeholder") + "");
                        }
                        $(inputs[i]).parent().next(".message_false").fadeIn();
                        end = false;
                    } else {
                        $(inputs[i]).removeClass("false_required");
                        $(inputs[i]).parent().next(".message_false").remove();
                    }
                }
            }
        }
        /* End */

        /* Phone */

        if ($(".saiForm").find("input[type='tel']").length != false) {

            $(".saiForm").find("input[type='tel']").each(function () {
                $(this).attr("type", "text").addClass("phone");
            });
        }

        function valid_tel() {
            var inputs = $(".saiForm").find(".phone.required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Enter phone number
");
                    } else {
                        $(inputs[i]).next().text("Enter phone number");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\+/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
At the beginning to be '+'
");
                    } else {
                        $(inputs[i]).next().text("At the beginning to be '+'");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if (!/^\+[0-9\s()-]+$/i.test($(inputs[i]).val())) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
Available characters '0-9 ( ) - '
");
                    } else {
                        $(inputs[i]).next().text("Available characters '0-9 ( ) - ' ");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else if ($(inputs[i]).val().match(/[0-9]/gi).length < 10) {
                    $(inputs[i]).addClass("false_required");
                    if (!$(inputs[i]).next(".message_false").length) {
                        $(inputs[i]).after("
The minimum number of 10
");
                    } else {
                        $(inputs[i]).next().text("The minimum number of 10");
                    }
                    $(inputs[i]).next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).removeClass("false_required");
                    $(inputs[i]).next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Radio */

        if ($(".saiForm").find("input[type='radio']").length != false) {

            $(".saiForm").find("input[type='radio']").each(function (index) {
                index = index + 1;

                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " radio" + index + "");
                } else {
                    $(this).attr("id", "radio" + index + "");
                }
                $(this).after("");

            });
        }

        function valid_radio() {
            var groups = $(".saiForm").find(".radio_group");
            var end = true;

            $(".saiForm").find(".radio_group").each(function (index) {
                var end2 = false;

                $(this).find(".required").each(function () {
                    if ($(this).is(":checked")) {
                        end2 = true;
                    }
                });

                if (!end2) {
                    $(this).find(".required").each(function () {
                        $(this).parent().addClass("false_required");
                    });
                    if (!$(this).next(".message_false").length) {
                        $(this).after("
Checked
");
                    } else {
                        $(this).next().text("Checked");
                    }
                    $(this).next(".message_false").fadeIn();

                    end = false;
                } else {
                    $(this).find(".radio").removeClass("false_required");
                    $(this).next(".message_false").remove();
                }
            });

            return end;
        }
        /* End */

        /* CheckBox */

        if ($(".saiForm").find("input[type='checkbox']").length != false) {

            $(".saiForm").find("input[type='checkbox']").each(function (index) {
                index = index + 1;
                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " checkbox" + index++ + "");
                } else {
                    $(this).attr("id", "checkbox" + index + "");
                }
                $(this).after("");

            });
        }

        function valid_checkbox() {
            var inputs = $(".saiForm").find(".checkbox .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if (!$(inputs[i]).is(":checked")) {
                    $(inputs[i]).parent().find("label").addClass("false_required");
                    if (!$(inputs[i]).parent().next(".message_false").length) {
                        $(inputs[i]).parent().after("
Checked
");
                    } else {
                        $(inputs[i]).parent().next().text("Checked");
                    }
                    $(inputs[i]).parent().next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).parent().find("label").removeClass("false_required");
                    $(inputs[i]).parent().next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* File */

        if ($(".saiForm").find("input[type='file']").length != false) {

            $(".saiForm").find("input[type='file']").each(function (index) {
                index = index + 1;
                $(this).wrap("

");
                if ($(this).attr("id") != undefined) {
                    $(this).attr("id", $(this).attr("id") + " file" + index++ + "");
                } else {
                    $(this).attr("id", "file" + index + "");
                }
                $(this).after("Browse...");
                $(this).parent().append("");
                $(this).on("change", function () {
                    $(this).parent().find("span").text($(this).val());
                });
            });
        }

        function valid_file() {
            var inputs = $(".saiForm").find(".file .required");
            var end = true;
            for (i = 0; i < inputs.length; i++) {
                if ($(inputs[i]).val() == false) {
                    $(inputs[i]).parent().find("label").addClass("false_required");
                    if (!$(inputs[i]).parent().next(".message_false").length) {
                        $(inputs[i]).parent().after("
Select file
");
                    } else {
                        $(inputs[i]).parent().next().text("Select file");
                    }
                    $(inputs[i]).parent().next(".message_false").fadeIn();
                    end = false;
                } else {
                    $(inputs[i]).parent().find("label").removeClass("false_required");
                    $(inputs[i]).parent().next(".message_false").remove();
                }
            }
            return end;
        }
        /* End */

        /* Submit */
        $(".saiForm").on("submit", function () {
            var valid = true;
            valid = valid_email();
            valid = valid_tel();
            valid = valid_select();
            valid = valid_text();
            valid = valid_number();
            valid = valid_checkbox();
            valid = valid_file();
            valid = valid_radio();
            return valid;
        });
        /* End */
    };
})(jQuery);<|endoftext|>


<commit_before>var db = require("./db");
var util = require("./util");
var srv = require("./server");
var api = require("./api");
var async = require("async");

var constraints = {docs:{pre:{}, post:{}}};
var plugins = {};
var plugouts = {};

/*
 * Load doc and user from db.
 */
function load_objects(ctx, ret_handler){

	async.parallel(
		[function(next){//load document

			if(ctx.params && ctx.params.wid){

				db.select(ctx.params.catalog, ctx.params.wid, function(err,doc){

					if(err)
						next(err,null);
					else if(!err && !doc)
						next({code:-1, message:"Document not found: #" + ctx.params.catalog + "/" + ctx.params.wid }, null);
					else{
						ctx.doc = doc;
						next();
					}
				});
			}else
				next();
		},

		function(next){//load user

			if(ctx.params && ctx.params.uid){

				db.select("users",ctx.params.uid,function(err,user){

					if(err)
						next(err);
					else if( !err && !user){
						next({code:-1, message:"User not found: #users/" + ctx.params.uid });
					}else{
						ctx.user = user;
						next();
					}
				});
			}else
				next();

		}],function(err){
			ret_handler(err);
		}
	);

}

/*
 * Add a plugin
 */
exports.add_plugin_in = function(proc_name, plugin_name, func_handler){

	if(!plugins[proc_name])
		plugins[proc_name] = {};

	if(func_handler == undefined && typeof plugin_name == "function"){

		func_handler = plugin_name;
		plugin_name = util.generate_rstring(10);
	}

	plugins[proc_name][plugin_name] = function(ctx, end_handler){

		func_handler(ctx, end_handler);
	}

	return exports;
}

/*
 * Add a plugout
 */
exports.add_plugin_out = function(proc_name, plugout_name, func_handler){

	if(!plugouts[proc_name])
		plugouts[proc_name] = {};

	if(func_handler == undefined && typeof plugout_name == "function"){

		func_handler = plugout_name;
		plugout_name = util.generate_rstring(10);
	}

	plugouts[proc_name][plugout_name] = function(ctx, end_handler){

		func_handler(ctx, end_handler);
	}

	return exports;

}
/*
 * Add constraint as a pre or post constraint.
 */
function add_constraint( constraints, proc_name, cons_name, func_handler){

	if(!constraints[proc_name])
		constraints[proc_name] = {};

	if(func_handler == undefined && typeof cons_name == "function"){

		func_handler = cons_name;
		cons_name = util.generate_rstring(10);
	}

	constraints[proc_name][cons_name] = function(ctx){

		return func_handler(ctx);

	};

	return exports;

}

/*
 * Plug constraint after the document load.
 */
exports.add_constraint_post =  function( proc_name, cons_name, func_handler, catalog_name ){

	catalog_name = catalog_name || srv.config.db.default_catalog;
	if( !constraints[catalog_name] )
		constraints[catalog_name] = {pre:{},post:{}};

	return add_constraint(constraints[catalog_name].post, proc_name, cons_name, func_handler);
}

/*
 * Plug constraint before the document load.
 */
exports.add_constraint_pre =  function( proc_name, cons_name, func_handler, catalog_name ){

	catalog_name = catalog_name || srv.config.db.default_catalog;
	if( !constraints[catalog_name] )
		constraints[catalog_name] = {pre:{},post:{}};

	return add_constraint(constraints[catalog_name].pre, proc_name, cons_name, func_handler);
}

/*
 * Execute all constaints for proc_name procedure, if no constraints returns an error object execution continues
 * calling api.remote[proc_name].
 */
exports.execute = function( proc_name, params, ret_handler){

	if( !api.remote[proc_name] ){

		ret_handler({code:-32601, message:"Method not found."},null);
		return;
    }else if(!srv.api.config.procedures[proc_name] ){

    	ret_handler({code:-32604, message:"Method not available."},null);
    	return;
    }else if(!srv.config.app || !srv.config.app.status){

    	ret_handler({code:-32603, message:"Server not started"});
    	return;
    }

    //params may come from another function, in that case params refers to ctx.
	var ctx = (params && params.params && params.config && params.doc)? params: { params:params||{},  config:{save:1, emit:1} };
	ctx.params.catalog = ctx.params.catalog || srv.config.db.default_catalog;

	//Execute plugins to transform ctx some way.
	async.forEachSeries(Object.keys(plugins[proc_name]||{}),function(plugin_name,end_handler){

		plugins[proc_name][plugin_name](ctx, end_handler);

	},function(){//plugins executed

		//constraints pre document load.
		if(constraints[ctx.params.catalog]){
		    for( key in constraints[ctx.params.catalog].pre[proc_name] ){

				var err = constraints[ctx.params.catalog].pre[proc_name][key](ctx);
				if(err){

					ret_handler(err,null);
					return;
				}
			}
		}

		load_objects(ctx, function(err){//document and user load

			ctx.doc = ctx.doc || {};

			if(err){
				ret_handler(err,null);
				return;
			}else{

				//constraints post document load
				if(constraints[ctx.params.catalog]){
					for( key in constraints[ctx.params.catalog].post[proc_name] ){

						var err = constraints[ctx.params.catalog].post[proc_name][key](ctx);
						if(err){

							ret_handler(err,null);
							return;
						}
					}
				}

				//All constraints satisfied and plugins executed, so execute procedure.
				api.remote[proc_name](ctx, function(err,val){

					ctx.retval = util.clone(val);//return value returned over ctx.
					async.series([
					function(next){//execute plugouts: may modify ctx.doc, ctx.retval and ctx.payload

						async.forEachSeries(Object.keys(plugouts[proc_name]||{}),function(plugout_name,end_handler){

							plugouts[proc_name][plugout_name](ctx, end_handler);

						},function(){
							next();
						});
					},
					function(next){
						if(err){
							ret_handler(err,null);
							return;
						}else if(ctx.config.save){

							async.series([

								function(next){
									if(ctx.user){
										db.save("users", ctx.user, function(err){

											if(err)
												next(err);
											else
												next();
										});
									}else
										next();
								},
								function(next){
									db.save(ctx.params.catalog, ctx.doc, function(err){

										if(err)
											next(err);
										else
											next();
									});
								}
							],function(err){
								if(err)
									ret_handler(err,null);
								else{
									if(ctx.config.emit){

										ctx.payload = ctx.payload || ctx.params;

										api.emit("ev_api_" + proc_name, ctx);
									}
									ret_handler(null,ctx);
								}
							});
						}else
							ret_handler(null,ctx);
					}
					]);

				});//end execute

			}

		});
	});

}

exports.constraints = {

	is_owner: function(ctx){

			if( ctx.params.uid != ctx.doc.uid ){

				return {code:-2, message:"No access permission: not owner"};
			}
	},

	has_joined: function(ctx){

		if( !ctx.doc.rcpts || ctx.doc.rcpts.indexOf(ctx.params.uid) == -1 ){

			return {code:-3, message:"No access permission: not joined"};
		}
	},

	not_joined: function(ctx){

		if( ctx.doc.rcpts.indexOf(ctx.params.uid) != -1 ){

			return {code:-4, message:"No access permission: already joined"};
		}
	},

	not_catalog: function(ctx){

		/*
		if( srv.config.db.system_catalogs.indexOf(ctx.params.catalog) != -1 ){

			return {code:-5, message:"No access permission: system catalog"};
		}
		*/
		return {code:-5, message:"No access permission: restricted catalog"};
	},

	user_catalog: function(ctx){

		if( srv.config.db.user_catalogs.indexOf(ctx.params.catalog) == -1 ){

			return {code:-6, message:"No access permission: not user catalog"};
		}
	},

	is_joinable: function(ctx){

		if(!ctx.doc.rcpts){

			return {code:-7, message:"No access permission: not joinable/unjoinable"};
		}
	},

	is_reserved: function(ctx){

		if(ctx.params.fname == "rcpts" || ctx.params.fname == "_id" || ctx.params.fname == "uid" || ctx.params.fname == "ctime" || ctx.params.fname == "etime" || ctx.params.fname == "catalog"){

			return {code:-8, message:"Reserved word not allowed as field name: " + ctx.params.fname };
		}
	},

	field_exists: function(ctx){

		if(ctx.params.fname){
			if( util.get_field(ctx.doc,ctx.params.fname) == undefined){

				return {code:-9, message:"Not exists: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname};
			}
		}
	},

	field_type: function(type){

			return function(ctx){

				var obj = util.get_field(ctx.doc,ctx.params.fname);

				if(type == "array"){

					if(typeof obj != "object" || !obj.push || !obj.pop || !obj.shift || !obj.unshift  ){

						return {code:-10, message:"Wrong type: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname + " not array"};
					}
				}else{

					if(typeof obj != type ){

						return {code:-10, message:"Wrong type: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname + " not " + type};
					}
				}
		}
	},

	param_type : function(param_name, type){

			return function(ctx){

				if(typeof ctx.params[param_name] != type ){

					return {code:-11, message:"Wrong parameter type: "+ param_name +" not " + type };
				}
		}
	},

	is_required: function(field){

			return function(ctx){

				if(!ctx.params[field]){

					return {code:-12, message:field  + " parameter required"};
				}
		}
	},

	is_protected:function(protected_fname){

		return function(ctx){

			var fname;
			if( util.is_array(ctx.params.fname))
				fname = ctx.params.fname;
			else
				fname = [ctx.params.fname];

			for(i=0; i < fname.length; i++){

				if(fname[i] == protected_fname){

					return {code:-13, message:"Protected field not allowed as field name: " + fname[i] };
				}
			}
		}
	}

};

exports.plugins = {

	url_transform : function(ctx,next){

		util.parse_url(ctx);
		next();
	},

	notifying_doc : function(ctx,next){

		if(ctx.params.notifiable == undefined){
			if(ctx.params.catalog == srv.config.db.default_catalog)
				ctx.params.rcpts = [ctx.params.uid];
		}else if(ctx.params.notifiable){
			ctx.params.rcpts = [ctx.params.uid];
		}
		next();
	},

	notifying_catalog:function(catalog){

		return function(ctx,next){

			if(ctx.params.catalog == catalog)
				ctx.params.rcpts = [ctx.params.uid];
			next();
		}
	},

	rewrite_id:function(ctx,next){

		if(ctx.retval._id){
			if(ctx.params.catalog == "users"){

				ctx.retval.uid = "" + ctx.retval._id;
				delete ctx.retval._id;
			}else{

				ctx.retval.wid = "" + ctx.retval._id;
				delete ctx.retval._id;
			}
		}
		next();
	},

	external_config: function(ctx,next){

		if(ctx.params.config){

			for(key in ctx.params.config){

				ctx.config[key] = ctx.params.config[key];
			}
			delete ctx.params.config;
		}

		next();
	}
}
<commit_msg>sandbox plugins can return error via ctx.err, and execution is stopped and error returned.<commit_after>var db = require("./db");
var util = require("./util");
var srv = require("./server");
var api = require("./api");
var async = require("async");

var constraints = {docs:{pre:{}, post:{}}};
var plugins = {};
var plugouts = {};

/*
 * Load doc and user from db.
 */
function load_objects(ctx, ret_handler){

	async.parallel(
		[function(next){//load document

			if(ctx.params && ctx.params.wid){

				db.select(ctx.params.catalog, ctx.params.wid, function(err,doc){

					if(err)
						next(err,null);
					else if(!err && !doc)
						next({code:-1, message:"Document not found: #" + ctx.params.catalog + "/" + ctx.params.wid }, null);
					else{
						ctx.doc = doc;
						next();
					}
				});
			}else
				next();
		},

		function(next){//load user

			if(ctx.params && ctx.params.uid){

				db.select("users",ctx.params.uid,function(err,user){

					if(err)
						next(err);
					else if( !err && !user){
						next({code:-1, message:"User not found: #users/" + ctx.params.uid });
					}else{
						ctx.user = user;
						next();
					}
				});
			}else
				next();

		}],function(err){
			ret_handler(err);
		}
	);

}

/*
 * Add a plugin
 */
exports.add_plugin_in = function(proc_name, plugin_name, func_handler){

	if(!plugins[proc_name])
		plugins[proc_name] = {};

	if(func_handler == undefined && typeof plugin_name == "function"){

		func_handler = plugin_name;
		plugin_name = util.generate_rstring(10);
	}

	plugins[proc_name][plugin_name] = function(ctx, end_handler){

		func_handler(ctx, end_handler);
	}

	return exports;
}

/*
 * Add a plugout
 */
exports.add_plugin_out = function(proc_name, plugout_name, func_handler){

	if(!plugouts[proc_name])
		plugouts[proc_name] = {};

	if(func_handler == undefined && typeof plugout_name == "function"){

		func_handler = plugout_name;
		plugout_name = util.generate_rstring(10);
	}

	plugouts[proc_name][plugout_name] = function(ctx, end_handler){

		func_handler(ctx, end_handler);
	}

	return exports;

}
/*
 * Add constraint as a pre or post constraint.
 */
function add_constraint( constraints, proc_name, cons_name, func_handler){

	if(!constraints[proc_name])
		constraints[proc_name] = {};

	if(func_handler == undefined && typeof cons_name == "function"){

		func_handler = cons_name;
		cons_name = util.generate_rstring(10);
	}

	constraints[proc_name][cons_name] = function(ctx){

		return func_handler(ctx);

	};

	return exports;

}

/*
 * Plug constraint after the document load.
 */
exports.add_constraint_post =  function( proc_name, cons_name, func_handler, catalog_name ){

	catalog_name = catalog_name || srv.config.db.default_catalog;
	if( !constraints[catalog_name] )
		constraints[catalog_name] = {pre:{},post:{}};

	return add_constraint(constraints[catalog_name].post, proc_name, cons_name, func_handler);
}

/*
 * Plug constraint before the document load.
 */
exports.add_constraint_pre =  function( proc_name, cons_name, func_handler, catalog_name ){

	catalog_name = catalog_name || srv.config.db.default_catalog;
	if( !constraints[catalog_name] )
		constraints[catalog_name] = {pre:{},post:{}};

	return add_constraint(constraints[catalog_name].pre, proc_name, cons_name, func_handler);
}

/*
 * Execute all constaints for proc_name procedure, if no constraints returns an error object execution continues
 * calling api.remote[proc_name].
 */
exports.execute = function( proc_name, params, ret_handler){

	if( !api.remote[proc_name] ){

		ret_handler({code:-32601, message:"Method not found."},null);
		return;
    }else if(!srv.api.config.procedures[proc_name] ){

    	ret_handler({code:-32604, message:"Method not available."},null);
    	return;
    }else if(!srv.config.app || !srv.config.app.status){

    	ret_handler({code:-32603, message:"Server not started"},null);
    	return;
    }

    //params may come from another function, in that case params refer to ctx.
	var ctx = (params && params.params && params.config && params.doc)? params: { params:params||{},  config:{save:1, emit:1} };
	ctx.params.catalog = ctx.params.catalog || srv.config.db.default_catalog;

	//Execute plugins to transform ctx some way.
	async.forEachSeries(Object.keys(plugins[proc_name]||{}),function(plugin_name,next){

		plugins[proc_name][plugin_name](ctx, function(){

			if(ctx.err){

				ret_handler(ctx.err,null);
				return;
			}
			next();
		});

	},function(){//plugins executed

		//constraints pre document load.
		if(constraints[ctx.params.catalog]){
		    for( key in constraints[ctx.params.catalog].pre[proc_name] ){

				var err = constraints[ctx.params.catalog].pre[proc_name][key](ctx);
				if(err){

					ret_handler(err,null);
					return;
				}
			}
		}

		load_objects(ctx, function(err){//document and user load

			ctx.doc = ctx.doc || {};

			if(err){
				ret_handler(err,null);
				return;
			}else{

				//constraints post document load
				if(constraints[ctx.params.catalog]){
					for( key in constraints[ctx.params.catalog].post[proc_name] ){

						var err = constraints[ctx.params.catalog].post[proc_name][key](ctx);
						if(err){

							ret_handler(err,null);
							return;
						}
					}
				}

				//All constraints satisfied and plugins executed, so execute procedure.
				api.remote[proc_name](ctx, function(err,val){

					ctx.retval = util.clone(val);//return value returned over ctx.
					async.series([
					function(next){//execute plugouts: may modify ctx.doc, ctx.retval and ctx.payload

						async.forEachSeries(Object.keys(plugouts[proc_name]||{}),function(plugout_name,end_handler){

							plugouts[proc_name][plugout_name](ctx, end_handler);

						},function(){
							next();
						});
					},
					function(next){
						if(err){
							ret_handler(err,null);
							return;
						}else if(ctx.config.save){

							async.series([

								function(next){
									if(ctx.user){
										db.save("users", ctx.user, function(err){

											if(err)
												next(err);
											else
												next();
										});
									}else
										next();
								},
								function(next){
									db.save(ctx.params.catalog, ctx.doc, function(err){

										if(err)
											next(err);
										else
											next();
									});
								}
							],function(err){
								if(err)
									ret_handler(err,null);
								else{
									if(ctx.config.emit){

										ctx.payload = ctx.payload || ctx.params;

										api.emit("ev_api_" + proc_name, ctx);
									}
									ret_handler(null,ctx);
								}
							});
						}else
							ret_handler(null,ctx);
					}
					]);

				});//end execute

			}

		});
	});

}

exports.constraints = {

	is_owner: function(ctx){

			if( ctx.params.uid != ctx.doc.uid ){

				return {code:-2, message:"No access permission: not owner"};
			}
	},

	has_joined: function(ctx){

		if( !ctx.doc.rcpts || ctx.doc.rcpts.indexOf(ctx.params.uid) == -1 ){

			return {code:-3, message:"No access permission: not joined"};
		}
	},

	not_joined: function(ctx){

		if( ctx.doc.rcpts.indexOf(ctx.params.uid) != -1 ){

			return {code:-4, message:"No access permission: already joined"};
		}
	},

	not_catalog: function(ctx){

		/*
		if( srv.config.db.system_catalogs.indexOf(ctx.params.catalog) != -1 ){

			return {code:-5, message:"No access permission: system catalog"};
		}
		*/
		return {code:-5, message:"No access permission: restricted catalog"};
	},

	user_catalog: function(ctx){

		if( srv.config.db.user_catalogs.indexOf(ctx.params.catalog) == -1 ){

			return {code:-6, message:"No access permission: not user catalog"};
		}
	},

	is_joinable: function(ctx){

		if(!ctx.doc.rcpts){

			return {code:-7, message:"No access permission: not joinable/unjoinable"};
		}
	},

	is_reserved: function(ctx){

		if(ctx.params.fname == "rcpts" || ctx.params.fname == "_id" || ctx.params.fname == "uid" || ctx.params.fname == "ctime" || ctx.params.fname == "etime" || ctx.params.fname == "catalog"){

			return {code:-8, message:"Reserved word not allowed as field name: " + ctx.params.fname };
		}
	},

	field_exists: function(ctx){

		if(ctx.params.fname){
			if( util.get_field(ctx.doc,ctx.params.fname) == undefined){

				return {code:-9, message:"Not exists: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname};
			}
		}
	},

	field_type: function(type){

			return function(ctx){

				var obj = util.get_field(ctx.doc,ctx.params.fname);

				if(type == "array"){

					if(typeof obj != "object" || !obj.push || !obj.pop || !obj.shift || !obj.unshift  ){

						return {code:-10, message:"Wrong type: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname + " not array"};
					}
				}else{

					if(typeof obj != type ){

						return {code:-10, message:"Wrong type: #" + ctx.params.catalog + "/" + ctx.params.wid + ":" + ctx.params.fname + " not " + type};
					}
				}
		}
	},

	param_type : function(param_name, type){

			return function(ctx){

				if(typeof ctx.params[param_name] != type ){

					return {code:-11, message:"Wrong parameter type: "+ param_name +" not " + type };
				}
		}
	},

	is_required: function(field){

			return function(ctx){

				if(!ctx.params[field]){

					return {code:-12, message:field  + " parameter required"};
				}
		}
	},

	is_protected:function(protected_fname){

		return function(ctx){

			var fname;
			if( util.is_array(ctx.params.fname))
				fname = ctx.params.fname;
			else
				fname = [ctx.params.fname];

			for(i=0; i < fname.length; i++){

				if(fname[i] == protected_fname){

					return {code:-13, message:"Protected field not allowed as field name: " + fname[i] };
				}
			}
		}
	}

};

exports.plugins = {

	url_transform : function(ctx,next){

		util.parse_url(ctx);
		next();
	},

	notifying_doc : function(ctx,next){

		if(ctx.params.notifiable == undefined){
			if(ctx.params.catalog == srv.config.db.default_catalog)
				ctx.params.rcpts = [ctx.params.uid];
		}else if(ctx.params.notifiable){
			ctx.params.rcpts = [ctx.params.uid];
		}
		next();
	},

	notifying_catalog:function(catalog){

		return function(ctx,next){

			if(ctx.params.catalog == catalog)
				ctx.params.rcpts = [ctx.params.uid];
			next();
		}
	},

	rewrite_id:function(ctx,next){

		if(ctx.retval._id){
			if(ctx.params.catalog == "users"){

				ctx.retval.uid = "" + ctx.retval._id;
				delete ctx.retval._id;
			}else{

				ctx.retval.wid = "" + ctx.retval._id;
				delete ctx.retval._id;
			}
		}
		next();
	},

	external_config: function(ctx,next){

		if(ctx.params.config){

			for(key in ctx.params.config){

				ctx.config[key] = ctx.params.config[key];
			}
			delete ctx.params.config;
		}

		next();
	}
}
<|endoftext|>


<commit_before>'use strict';

var util = require('util'),
    mdns = require('mdns2'),
    events = require('events'),
    logger = require('./logger'),
    CastDevice = require('./device');

function Scanner(options) {
  var self = this;
  events.EventEmitter.call(this);
  this.logger = options.logger || logger.devnull;
  this.timeout = options.timeout || 5000;
  this.browser = mdns.createBrowser(mdns.tcp('googlecast'));
  this.browser
    .on('serviceUp', onServiceUp)
    .on('serviceDown', onServiceDown);
  this.devices = [];

  function onServiceUp(device) {
    if (!device.addresses ||
        device.addresses.length === 0 ||
        !device.name ||
        !device.port ||
        !device.txtRecord ||
        !device.txtRecord.fn ||
        !device.txtRecord.id) {
      this.logger.info('New device discovered but missing some mandatory information',
                       device);
      return;
    }
    // Usually, Chromecast device are using IPv4 only.
    var c = new CastDevice({
      friendlyName: device.txtRecord.fn,
      address: device.addresses[0],
      port: device.port,
      id: device.txtRecord.id,
      logger: this.logger,
      timeout: this.timeout
    });
    self.devices[c.id] = c;
    self.emit('online', c);
  }
  function onServiceDown(device) {
    var id = (device.txtRecord || {}).id;
    if (!self.devices[id]) {
      this.logger.debug('Unknown device has disappeared', device);
      return;
    }
    self.emit('offline', self.devices[id]);
    delete self.devices[id];
  }
}
util.inherits(Scanner, events.EventEmitter);

Scanner.prototype.start = function() {
  this.browser.start();
  return this;
};
Scanner.prototype.end = function() {
  this.browser.end();
  return this;
};

module.exports = Scanner;
<commit_msg>scanner: don't fire `online` event if already online<commit_after>'use strict';

var util = require('util'),
    mdns = require('mdns2'),
    events = require('events'),
    logger = require('./logger'),
    CastDevice = require('./device');

function Scanner(options) {
  var self = this;
  events.EventEmitter.call(this);
  this.logger = options.logger || logger.devnull;
  this.timeout = options.timeout || 5000;
  this.browser = mdns.createBrowser(mdns.tcp('googlecast'));
  this.browser
    .on('serviceUp', onServiceUp)
    .on('serviceDown', onServiceDown);
  this.devices = [];

  function onServiceUp(device) {
    if (!device.addresses ||
        device.addresses.length === 0 ||
        !device.name ||
        !device.port ||
        !device.txtRecord ||
        !device.txtRecord.fn ||
        !device.txtRecord.id) {
      this.logger.info('New device discovered but missing some mandatory information',
                       device);
      return;
    }
    // Usually, Chromecast device are using IPv4 only.
    var c = new CastDevice({
      friendlyName: device.txtRecord.fn,
      address: device.addresses[0],
      port: device.port,
      id: device.txtRecord.id,
      logger: this.logger,
      timeout: this.timeout
    });
    if (self.devices[c.id]) {
      this.logger.debug('Already online device appeared', device);
      return;
    }
    self.devices[c.id] = c;
    self.emit('online', c);
  }
  function onServiceDown(device) {
    var id = (device.txtRecord || {}).id;
    if (!self.devices[id]) {
      this.logger.debug('Unknown device has disappeared', device);
      return;
    }
    self.emit('offline', self.devices[id]);
    delete self.devices[id];
  }
}
util.inherits(Scanner, events.EventEmitter);

Scanner.prototype.start = function() {
  this.browser.start();
  return this;
};
Scanner.prototype.end = function() {
  this.browser.end();
  return this;
};

module.exports = Scanner;
<|endoftext|>


<commit_before>'use strict';

const origins = {
  prod: {
    platform: 'https://platform.vin.li',
    telemetry: 'https://telemetry.vin.li',
    event: 'https://events.vin.li',
    trip: 'https://trips.vin.li',
    dev: 'https://dev.vin.li',
    auth: 'https://auth.vin.li',
    rule: 'https://rules.vin.li',
    dummy: 'https://dummies.vin.li',
    stream: 'wss://stream.vin.li'
  }
};

module.exports = function(service, stack) {
  return origins[stack || 'prod'][service];
};
<commit_msg>stack from env<commit_after>'use strict';

const origins = (stack) => ({
  platform: `https://platform${stack}`,
  telemetry: `https://telemetry${stack}`,
  event: `https://events${stack}`,
  trip: `https://trips${stack}`,
  dev: `https://dev${stack}`,
  auth: `https://auth${stack}`,
  rule: `https://rules${stack}`,
  dummy: `https://dummies${stack}`,
  stream: `wss://stream${stack}`
});

module.exports = function(service, stack) {
  return origins(stack || process.env.VINLI_STACK || '.vin.li')[service];
};
<|endoftext|>


<commit_before>var utils = require('./client/utils');
var Cache = require('./cache');
var Request = require('./request');
var Socket = require('./client/socket');

// TODO make sessions expire
var cookie = require('cookie');

var sessionCache = Cache('sessions');
var publicSid = utils.uid();
var sessionCookieKey = 'sid';

/**
 * Create or get a session form a request/connection.
 *
 * @public
 * @param {object} The request or connection object.
 * @param {object} The http response object.
 */
module.exports = function (connection, response) {

    // make it compatible with ws connections
    var con = connection.upgradeReq ? connection.upgradeReq : connection;

    // create an empty session
    var session;

    // parse cookies, get session id
    if (con.headers.cookie) {

        var parsedCookie = cookie.parse(con.headers.cookie);

        if (parsedCookie && parsedCookie[sessionCookieKey]) {

            // get session
            session = sessionCache.get(parsedCookie[sessionCookieKey]);

            // remove cookie from http headers
            if (!session) {
                connection._sidInvalid = true;
            }
        }
    }

    // create a default session, if no session exists
    session = session || utils.clone(Session);

    // update the expire date time
    session.touch();

    // call the websocket connection hanlder
    if (connection.upgradeReq) {

        /**
         * Handle websocket message.
         *
         * @private
         * @param {sring} The message data.
         */
        connection.on('message', function (message) {
            Socket.message(message, connection, session);
        });

        return;
    }

    // call the http request handler
    Request(session, connection, response);
};

/**
 * Session object.
 *
 * @class Session
 */
var Session = {

    /**
     * Clone object. True prototypal inheritance.
     *
     * @public
     * @param {string} The role name.
     * @param {string} The locale.
     * @param {string} The user ID.
     * @param {function} The callback handler.
     */
    create: function (role, locale, user, callback) {
        var self = this;

        self.sid = utils.uid();
        self[engine.session_role] = role;
        self[engine.session_user] = user;
        self[engine.session_locale] = locale || 'en_US';

        // save session
        self.save(callback);
    },

    /**
     * Destroy the session.
     *
     * @public
     * @param {function} The callback handler.
     */
    destroy: function (callback) {
        var self = this;

        // remove from cache
        sessionCache.rm(self.sid);

        // remove session data
        delete self.sid;
        delete self[engine.session_role];
        delete self[engine.session_user];
        delete self[engine.session_locale];

        callback();
    },

    /**
     * Update/save the session data to the cache.
     *
     * @public
     * @param {function} The callback handler.
     */
    save: function (callback) {
        var self = this;

        // save session
        if (self.sid) {
            sessionCache.set(self.sid, self);
        }

        callback();
    },

    /**
     * Renew the session ID.
     *
     * @public
     * @todo Renew session with a new sid.
     */
    regenerate: function () {
        // TODO renew session id
    },

    /**
     * Update the expire date to now.
     *
     * @public
     * @todo Update the expire date on the session
     */
    touch: function () {
        // TODO update expire
    }
};
<commit_msg>use socket.parse to handle sever ws messages<commit_after>var utils = require('./client/utils');
var Cache = require('./cache');
var Request = require('./request');
var Socket = require('./client/socket');

// TODO make sessions expire
var cookie = require('cookie');

var sessionCache = Cache('sessions');
var publicSid = utils.uid();
var sessionCookieKey = 'sid';

/**
 * Create or get a session form a request/connection.
 *
 * @public
 * @param {object} The request or connection object.
 * @param {object} The http response object.
 */
module.exports = function (connection, response) {

    // make it compatible with ws connections
    var con = connection.upgradeReq ? connection.upgradeReq : connection;

    // create an empty session
    var session;

    // parse cookies, get session id
    if (con.headers.cookie) {

        var parsedCookie = cookie.parse(con.headers.cookie);

        if (parsedCookie && parsedCookie[sessionCookieKey]) {

            // get session
            session = sessionCache.get(parsedCookie[sessionCookieKey]);

            // remove cookie from http headers
            if (!session) {
                connection._sidInvalid = true;
            }
        }
    }

    // create a default session, if no session exists
    session = session || utils.clone(Session);

    // update the expire date time
    session.touch();

    // call the websocket connection hanlder
    if (connection.upgradeReq) {

        /**
         * Handle websocket message.
         *
         * @private
         * @param {sring} The message data.
         */
        connection.on('message', function (message) {
            Socket.parse(message, connection, session);
        });

        return;
    }

    // call the http request handler
    Request(session, connection, response);
};

/**
 * Session object.
 *
 * @class Session
 */
var Session = {

    /**
     * Clone object. True prototypal inheritance.
     *
     * @public
     * @param {string} The role name.
     * @param {string} The locale.
     * @param {string} The user ID.
     * @param {function} The callback handler.
     */
    create: function (role, locale, user, callback) {
        var self = this;

        self.sid = utils.uid();
        self[engine.session_role] = role;
        self[engine.session_user] = user;
        self[engine.session_locale] = locale || 'en_US';

        // save session
        self.save(callback);
    },

    /**
     * Destroy the session.
     *
     * @public
     * @param {function} The callback handler.
     */
    destroy: function (callback) {
        var self = this;

        // remove from cache
        sessionCache.rm(self.sid);

        // remove session data
        delete self.sid;
        delete self[engine.session_role];
        delete self[engine.session_user];
        delete self[engine.session_locale];

        callback();
    },

    /**
     * Update/save the session data to the cache.
     *
     * @public
     * @param {function} The callback handler.
     */
    save: function (callback) {
        var self = this;

        // save session
        if (self.sid) {
            sessionCache.set(self.sid, self);
        }

        callback();
    },

    /**
     * Renew the session ID.
     *
     * @public
     * @todo Renew session with a new sid.
     */
    regenerate: function () {
        // TODO renew session id
    },

    /**
     * Update the expire date to now.
     *
     * @public
     * @todo Update the expire date on the session
     */
    touch: function () {
        // TODO update expire
    }
};
<|endoftext|>


<commit_before>var util   = require('util')
  , crypto = require('crypto')
  , _      = require('underscore');

var sqs             = {}  // This will be set in the module nitializer
  , queueAttributes = {}  // As will be this.
  , queuePrefix     = '' // And as will be this.
  , longPoll        = false;

/**
* Module initializer
*
* @param AWSConfig - configuration to connect to SQS on AWS.
*   Required fields:
*    - awsAccountId : AWS Account ID.
*    - accessKeyId : AWS access key ID.
*    - secretAccessKey : AWS secret access key.
*    - region. This value must correspond to the ones defined in awssum.io library's amazon/amazon module: http://awssum.io/amazon/
               Region defaults to amazon.US_EAST_1, since it still seems to be the most common one.
*    - prefix: A unique prefix that will be used in combination with topic names to name queues used in Task Scheduler. Defaults to: "task"
*    - AttributeName[] and AttributeValue[]: allows configuration of topic queue values in param-array-set syntax.
        @See: http://awssum.io/amazon/sqs/create-queue.html for the syntax
        @See: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/Query_QueryCreateQueue.html for allowed values
*
*
* @return
*   true: if success, err object: if SQS connection cannot be initialized or another error occured.
*
*/
exports = module.exports = function(AWSConfig) {

  // Ensure proper initialization
  if (!AWSConfig || typeof AWSConfig.accessKeyId === 'undefined' ||
                    typeof AWSConfig.secretAccessKey === 'undefined' ||
                    typeof AWSConfig.awsAccountId === 'undefined') {

    throw new Error("You must initialize sqstask module with a configuration object that has properly \
defined accessKeyId, secretAccessKey and awsAccountID fields, coresponding to core AWS credentials");

  }

  if(AWSConfig.longpoll && _.isNumber(AWSConfig.longpoll)) {
    longPoll = AWSConfig.longpoll;
  }

  if (typeof AWSConfig.AttributeName !== 'undefined' && typeof AWSConfig.AttributeValue !== 'undefined') {
    queueAttributes.names  = AWSConfig.AttributeName;
    queueAttributes.values = AWSConfig.AttributeValue;
  } else {
    queueAttributes.names  = ['MessageRetentionPeriod', 'VisibilityTimeout'];
    queueAttributes.values = [ 3600  // 1 hour retention
                             , 120   // 2 minutes visibility timeout. Most jobs should complete in under 2 minutes.
                             ];
    if(longPoll) {
      queueAttributes.names.push('ReceiveMessageWaitTimeSeconds');
      queueAttributes.values.push(longPoll);
    }
  }

  var awssum = require('awssum')
    , amazon = require('awssum-amazon')
    , Sqs = require('awssum-amazon-sqs').Sqs;

  // Defined at the module level, initialized here:
  queuePrefix = AWSConfig.queuePrefix || "task";

  // Defined at the module level, initialized here:
  var options = {
    'accessKeyId'     : AWSConfig.accessKeyId
  , 'secretAccessKey' : AWSConfig.secretAccessKey
  , 'awsAccountId'    : AWSConfig.awsAccountId
  , 'region'          : AWSConfig.region || amazon.US_EAST_1
  };

  sqs = new Sqs(options);

  return exports;

};

/**
 * Straightforward: send a message to the queue.  We do a little checking to
 * ensure that it worked (since SQS sends us an MD5 hash of the message), but no
 * information is returned except 'true', unless something went wrong that we
 * might be able to fix up the chain.
 */
exports.put = function(topic, message, callback) {

  var queueName = queuePrefix + "_" + topic;

  var body    = pack(message)
  , options = { QueueName   : queueName
              , MessageBody : body
              }
  , hasher  = crypto.createHash('md5');

  hasher.update(body, 'utf8');
  var md5 = hasher.digest('hex');

  sqs.SendMessage(options, function sendMessageToQueueCallback(err, data) {

    //console.dir("Send message returne! with: ");
    //console.dir(err);
    //console.dir(data);

    if (err) {
      callback(new Error(util.inspect(err.Body.ErrorResponse.Error)));
    } else if (  typeof data.Body.SendMessageResponse === "undefined"
              || typeof data.Body.SendMessageResponse.SendMessageResult === "undefined"
              || typeof data.Body.SendMessageResponse.SendMessageResult.MD5OfMessageBody === "undefined"
              || data.Body.SendMessageResponse.SendMessageResult.MD5OfMessageBody !== md5) {
      err = new Error('Message corrupted; did not successfully get sent!');
      callback(err);
    } else {
      callback(null);
    }
  });

};

/**
 * Receives a message and passes it to the callback.  Callbacks that need to
 * delete the message received after processing must accept three parameters,
 * the third being the thunk to call to delete the message once that is needed.
 * Parameters:
 *  err - no surprise
 *  message - the JSON message
 *  delFunc - the thunk that can be called to delete this message
 *
 * Right now there is no way to get a ReceiptHandle except contained by the
 * thunk. We can make a lower level exports.getRaw or something if we need other
 * metadata.
 *
 * It would also be convenient if we could pass in a visibility timeout and
 * expect that the thunk would fail without trying if it was past the timeout,
 * since relying on it to work after that is dangerous.
 */
exports.get = function(topic, callback) {
  var options = {QueueName: queuePrefix + "_" + topic};
  if(longPoll) {
    options.WaitTimeSeconds = longPoll
  }
  sqs.ReceiveMessage(options, function getMessageFromQueueCallback(err, data) {
    var receiptHandle   = ""
      , message         = null;

    if (err) {
      callback(err);
      return;
    }

    if (  typeof data.Body.ReceiveMessageResponse !== "undefined"
              && typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult !== "undefined"
              && typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message !== "undefined") {

      if (typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.Body !== "undefined" ) {
        message = unpack(data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.Body);
      }

      if (typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.ReceiptHandle !== "undefined" ) {
        receiptHandle = data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.ReceiptHandle;
      }
    }

    var oMessage        = new exports.Message;
        oMessage.body   = message;
        oMessage.id     = receiptHandle;
        oMessage.topic  = topic;
    callback(null, oMessage);
  });
};

/**
* Check if a topic exists, create if it doesn't
*
* This is not error-proof, because per AWS:
* "You must wait 60 seconds after deleting a queue before you can create another with the same name."
*/
exports.topicEnsureExists = function (topic, callback) {

  var name = queuePrefix + "_" + topic

  queueExists(name, function ensureQueueCheckCallback(err) {
    if (err) { // Probably doesn't exist, let's try to create
      queueCreate(name, function ensureQueueCreateCallback(err2) {
        if (err2) {
          callback(new Error(util.inspect(err2.Body.ErrorResponse.Error)));
        } else {
          callback(null);
        }
        return;
      });
      return;
    }
    callback(null);
  });
}

exports.Message = function (topic, body, id) {
  this.topic = topic || null;
  this.body = body || "";
  this.id   = id || null;
}

exports.Message.prototype.del     = function(callback) {

  if (!this.topic) { callback(new Error("Cannot delete a message with empty topic.")); return; }
  if (!this.id)    { callback(new Error("Cannot delete a message with empty ID.")); return; }

  deleteMessage(this.topic, this.id, function(err) {
    callback(err);
    return
  });

};

exports.Message.prototype.release = function(callback) {
  self = this;

  // Unfortunately, SQS has no concept of releasing a locked message
  // so to we ought to to delete and re-send the message :(
  deleteMessage(self.topic, self.id, function releaseDeleteCallback(err) {
    if (!err) {
      exports.put(self.topic, self.body, function releaseResendCallback(err2) {

        // After releasing, current message seizes to exist!
        self.topic = "";
        self.body = "";
        self.id = null;

        callback(err2);
        return;
      });
    } else {
      callback(new Error(util.inspect(err.Body.ErrorResponse.Error)));
      return;
    }
  });
};

//---- Private functions.
//---- These are defined as regular functions so that they don't need to be declared before they are used!

function deleteMessage(topic, receiptHandle, callback) {
  var options = { QueueName     : queuePrefix + "_" + topic
                , ReceiptHandle : receiptHandle
                };
  sqs.DeleteMessage(options, function(err, data) {
    if (err) {
      callback(new Error(util.inspect(err.Body.ErrorResponse.Error)));
    } else if (data.StatusCode !== 200) {
      err = new Error('Delete failed for some unknown reason!');
      callback(err);
    } else {
      callback(null);
    }
  });
}

function queueExists(name, callback) {
  var params = {
    QueueName       : name
  , AttributeName   : ['CreatedTimestamp']
  };

  sqs.GetQueueAttributes(params, function checkQueueExistsCallback(err, data) {
    callback(err);
  });
}

function queueCreate(name, callback) {

  var params = {
      QueueName       : name
    , AttributeName   : queueAttributes.names
    , AttributeValue  : queueAttributes.values
  };

  sqs.CreateQueue(params, function createQueueCallback(err, data) {
    callback(err);
  });
}

/**
 * Pack and unpack are here to keep whatever shell we use for messages all in
 * one place; this shell shouldn't be visible outside this module.
 */
function pack(message) {
  var body = {message: message} // doing this so we can attach other metadata if necessary
  //return JSON.stringify(body);
  return util.format('%j', body);
};

/**
 * Pack and unpack are here to keep whatever shell we use for messages all in
 * one place; this shell shouldn't be visible outside this module.
 */
function unpack(bodyString) {
  try {
    var body = JSON.parse(bodyString);
    if (typeof body.message !== "undefined") {
      return body.message;
    } else {
      var err = new Error("Didn't get a message; instead got:" + bodyString);
      return err;
    }
  } catch (err) {
    return new Error("Error parsing JSON message from the queue. Probably invalid JSON: " + bodyString);
  }
};
<commit_msg>Changing error message issue so that bugs in reporting errors doesn't effect code<commit_after>var util   = require('util')
  , crypto = require('crypto')
  , _      = require('underscore');

var sqs             = {}  // This will be set in the module nitializer
  , queueAttributes = {}  // As will be this.
  , queuePrefix     = '' // And as will be this.
  , longPoll        = false;

/**
* Module initializer
*
* @param AWSConfig - configuration to connect to SQS on AWS.
*   Required fields:
*    - awsAccountId : AWS Account ID.
*    - accessKeyId : AWS access key ID.
*    - secretAccessKey : AWS secret access key.
*    - region. This value must correspond to the ones defined in awssum.io library's amazon/amazon module: http://awssum.io/amazon/
               Region defaults to amazon.US_EAST_1, since it still seems to be the most common one.
*    - prefix: A unique prefix that will be used in combination with topic names to name queues used in Task Scheduler. Defaults to: "task"
*    - AttributeName[] and AttributeValue[]: allows configuration of topic queue values in param-array-set syntax.
        @See: http://awssum.io/amazon/sqs/create-queue.html for the syntax
        @See: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/Query_QueryCreateQueue.html for allowed values
*
*
* @return
*   true: if success, err object: if SQS connection cannot be initialized or another error occured.
*
*/
exports = module.exports = function(AWSConfig) {

  // Ensure proper initialization
  if (!AWSConfig || typeof AWSConfig.accessKeyId === 'undefined' ||
                    typeof AWSConfig.secretAccessKey === 'undefined' ||
                    typeof AWSConfig.awsAccountId === 'undefined') {

    throw new Error("You must initialize sqstask module with a configuration object that has properly \
defined accessKeyId, secretAccessKey and awsAccountID fields, coresponding to core AWS credentials");

  }

  if(AWSConfig.longpoll && _.isNumber(AWSConfig.longpoll)) {
    longPoll = AWSConfig.longpoll;
  }

  if (typeof AWSConfig.AttributeName !== 'undefined' && typeof AWSConfig.AttributeValue !== 'undefined') {
    queueAttributes.names  = AWSConfig.AttributeName;
    queueAttributes.values = AWSConfig.AttributeValue;
  } else {
    queueAttributes.names  = ['MessageRetentionPeriod', 'VisibilityTimeout'];
    queueAttributes.values = [ 3600  // 1 hour retention
                             , 120   // 2 minutes visibility timeout. Most jobs should complete in under 2 minutes.
                             ];
    if(longPoll) {
      queueAttributes.names.push('ReceiveMessageWaitTimeSeconds');
      queueAttributes.values.push(longPoll);
    }
  }

  var awssum = require('awssum')
    , amazon = require('awssum-amazon')
    , Sqs = require('awssum-amazon-sqs').Sqs;

  // Defined at the module level, initialized here:
  queuePrefix = AWSConfig.queuePrefix || "task";

  // Defined at the module level, initialized here:
  var options = {
    'accessKeyId'     : AWSConfig.accessKeyId
  , 'secretAccessKey' : AWSConfig.secretAccessKey
  , 'awsAccountId'    : AWSConfig.awsAccountId
  , 'region'          : AWSConfig.region || amazon.US_EAST_1
  };

  sqs = new Sqs(options);

  return exports;

};

/**
 * Straightforward: send a message to the queue.  We do a little checking to
 * ensure that it worked (since SQS sends us an MD5 hash of the message), but no
 * information is returned except 'true', unless something went wrong that we
 * might be able to fix up the chain.
 */
exports.put = function(topic, message, callback) {

  var queueName = queuePrefix + "_" + topic;

  var body    = pack(message)
  , options = { QueueName   : queueName
              , MessageBody : body
              }
  , hasher  = crypto.createHash('md5');

  hasher.update(body, 'utf8');
  var md5 = hasher.digest('hex');

  sqs.SendMessage(options, function sendMessageToQueueCallback(err, data) {

    //console.dir("Send message returne! with: ");
    //console.dir(err);
    //console.dir(data);

    if (err) {
      callback(err);
    } else if (  typeof data.Body.SendMessageResponse === "undefined"
              || typeof data.Body.SendMessageResponse.SendMessageResult === "undefined"
              || typeof data.Body.SendMessageResponse.SendMessageResult.MD5OfMessageBody === "undefined"
              || data.Body.SendMessageResponse.SendMessageResult.MD5OfMessageBody !== md5) {
      err = new Error('Message corrupted; did not successfully get sent!');
      callback(err);
    } else {
      callback(null);
    }
  });

};

/**
 * Receives a message and passes it to the callback.  Callbacks that need to
 * delete the message received after processing must accept three parameters,
 * the third being the thunk to call to delete the message once that is needed.
 * Parameters:
 *  err - no surprise
 *  message - the JSON message
 *  delFunc - the thunk that can be called to delete this message
 *
 * Right now there is no way to get a ReceiptHandle except contained by the
 * thunk. We can make a lower level exports.getRaw or something if we need other
 * metadata.
 *
 * It would also be convenient if we could pass in a visibility timeout and
 * expect that the thunk would fail without trying if it was past the timeout,
 * since relying on it to work after that is dangerous.
 */
exports.get = function(topic, callback) {
  var options = {QueueName: queuePrefix + "_" + topic};
  if(longPoll) {
    options.WaitTimeSeconds = longPoll
  }
  sqs.ReceiveMessage(options, function getMessageFromQueueCallback(err, data) {
    var receiptHandle   = ""
      , message         = null;

    if (err) {
      callback(err);
      return;
    }

    if (  typeof data.Body.ReceiveMessageResponse !== "undefined"
              && typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult !== "undefined"
              && typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message !== "undefined") {

      if (typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.Body !== "undefined" ) {
        message = unpack(data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.Body);
      }

      if (typeof data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.ReceiptHandle !== "undefined" ) {
        receiptHandle = data.Body.ReceiveMessageResponse.ReceiveMessageResult.Message.ReceiptHandle;
      }
    }

    var oMessage        = new exports.Message;
        oMessage.body   = message;
        oMessage.id     = receiptHandle;
        oMessage.topic  = topic;
    callback(null, oMessage);
  });
};

/**
* Check if a topic exists, create if it doesn't
*
* This is not error-proof, because per AWS:
* "You must wait 60 seconds after deleting a queue before you can create another with the same name."
*/
exports.topicEnsureExists = function (topic, callback) {

  var name = queuePrefix + "_" + topic

  queueExists(name, function ensureQueueCheckCallback(err) {
    if (err) { // Probably doesn't exist, let's try to create
      queueCreate(name, function ensureQueueCreateCallback(err2) {
        if (err2) {
          callback(new Error(util.inspect(err2.Body.ErrorResponse.Error)));
        } else {
          callback(null);
        }
        return;
      });
      return;
    }
    callback(null);
  });
}

exports.Message = function (topic, body, id) {
  this.topic = topic || null;
  this.body = body || "";
  this.id   = id || null;
}

exports.Message.prototype.del     = function(callback) {

  if (!this.topic) { callback(new Error("Cannot delete a message with empty topic.")); return; }
  if (!this.id)    { callback(new Error("Cannot delete a message with empty ID.")); return; }

  deleteMessage(this.topic, this.id, function(err) {
    callback(err);
    return
  });

};

exports.Message.prototype.release = function(callback) {
  self = this;

  // Unfortunately, SQS has no concept of releasing a locked message
  // so to we ought to to delete and re-send the message :(
  deleteMessage(self.topic, self.id, function releaseDeleteCallback(err) {
    if (!err) {
      exports.put(self.topic, self.body, function releaseResendCallback(err2) {

        // After releasing, current message seizes to exist!
        self.topic = "";
        self.body = "";
        self.id = null;

        callback(err2);
        return;
      });
    } else {
      callback(new Error(util.inspect(err.Body.ErrorResponse.Error)));
      return;
    }
  });
};

//---- Private functions.
//---- These are defined as regular functions so that they don't need to be declared before they are used!

function deleteMessage(topic, receiptHandle, callback) {
  var options = { QueueName     : queuePrefix + "_" + topic
                , ReceiptHandle : receiptHandle
                };
  sqs.DeleteMessage(options, function(err, data) {
    if (err) {
      callback(new Error(util.inspect(err.Body.ErrorResponse.Error)));
    } else if (data.StatusCode !== 200) {
      err = new Error('Delete failed for some unknown reason!');
      callback(err);
    } else {
      callback(null);
    }
  });
}

function queueExists(name, callback) {
  var params = {
    QueueName       : name
  , AttributeName   : ['CreatedTimestamp']
  };

  sqs.GetQueueAttributes(params, function checkQueueExistsCallback(err, data) {
    callback(err);
  });
}

function queueCreate(name, callback) {

  var params = {
      QueueName       : name
    , AttributeName   : queueAttributes.names
    , AttributeValue  : queueAttributes.values
  };

  sqs.CreateQueue(params, function createQueueCallback(err, data) {
    callback(err);
  });
}

/**
 * Pack and unpack are here to keep whatever shell we use for messages all in
 * one place; this shell shouldn't be visible outside this module.
 */
function pack(message) {
  var body = {message: message} // doing this so we can attach other metadata if necessary
  //return JSON.stringify(body);
  return util.format('%j', body);
};

/**
 * Pack and unpack are here to keep whatever shell we use for messages all in
 * one place; this shell shouldn't be visible outside this module.
 */
function unpack(bodyString) {
  try {
    var body = JSON.parse(bodyString);
    if (typeof body.message !== "undefined") {
      return body.message;
    } else {
      var err = new Error("Didn't get a message; instead got:" + bodyString);
      return err;
    }
  } catch (err) {
    return new Error("Error parsing JSON message from the queue. Probably invalid JSON: " + bodyString);
  }
};
<|endoftext|>


<commit_before>var crypto = require('crypto'),
	_ = require('underscore');
var SSHPacket = require('./ssh-packet'),
	SSHKex = require('./ssh-kex'),
	SSHUserAuth = require('./ssh-userauth'),
	SSHChannel = require('./ssh-channel');

var state = {
	PROTOCOL_VERSION_EXCHANGE: 'PROTOCOL_VERSION_EXCHANGE',
	KEX: 'KEX',
	SERVICE_REQUEST: 'SERVICE_REQUEST',
	USER_AUTH: 'USER_AUTH',
	CHANNEL: 'CHANNEL'
};

function TLP (conn, al) {
	this._conn = conn;
	this._al = al;

	this._conn.on('data', _.bind(function (buf) {
		this._packet.append(buf);
		this._process();
	}, this));
	this._state = state.PROTOCOL_VERSION_EXCHANGE;

	this._packet = new SSHPacket.SSHPacket();
	this._kex = new SSHKex.SSHKex();
	this._user = new SSHUserAuth.SSHUserAuth();
	this._user.onUserAuth(_.bind(function (request, response) {
		this._al.emit('userauth', request, response);
	}, this));
	this._channel = new SSHChannel.SSHChannel();
	this._channel.onData(_.bind(function (request, response) {
		this._al.emit('data', request, response);
	}, this));
}

var MESSAGE_NUMBER = {
	SSH_MSG_SERVICE_REQUEST: 5, 			/* RFC 4253 SSH Transport Layer Protocol Section 12 */
	SSH_MSG_SERVICE_ACCEPT: 6, 				/* RFC 4253 SSH Transport Layer Protocol Section 12 */
};

module.exports = TLP;

TLP.prototype._process = function () {
	console.log(this._state, this._packet.length);
	switch(this._state) {
	case state.PROTOCOL_VERSION_EXCHANGE:
		this._exchangeProtocolVersion(this._packet);
		this._state = state.KEX
		if(this._packet.eob()) break;
	case state.KEX:
		if(!this._packet.isReadable) return;

		this._kex.exchangeKeys(this._packet, _.bind(function (err, out) {
			if(this._kex.keyExchanged) {
				this._packet.attachKeyFunctions(
						this._kex.recvhashsize(), this._kex.recvblocksize(), this._kex.recvhmacf(), this._kex.decipherf(),
						this._kex.sendhashsize(), this._kex.sendblocksize(), this._kex.sendhmacf(), this._kex.cipherf());
				this._state = state.SERVICE_REQUEST;
			}

			this._packet.trim();
			this._conn.write(out);
		}, this));

		break;
	case state.SERVICE_REQUEST:
		if(!this._packet.isReadable) return;

		var payload = this._packet.readBinaryPacket();
		if(this._packet.disconnect_msg) return this._disconnect(this._packet.disconnect_msg);
		else if(this._packet.error_msg) return this._disconnect(this._packet.error_msg);
		else if(!payload) return;

		if(payload.readUInt8(0) === MESSAGE_NUMBER.SSH_MSG_SERVICE_REQUEST) {
			this._state = state.USER_AUTH;
			payload = payload.slice(1, payload.length);
			if(SSHPacket.readString(payload).string.toString('ascii') === 'ssh-userauth') {
				var user_auth = SSHPacket.createString(new Buffer('ssh-userauth', 'ascii'));
				this._conn.write(this._packet.createBinaryPacket(MESSAGE_NUMBER.SSH_MSG_SERVICE_ACCEPT, SSHPacket.createString(user_auth)));
			} else {
				throw new Error('unknown service requested: ' + payload);
			}
		} else
			throw new Error('unknown request: ' + payload.readUInt8(0));
		break;
	case state.USER_AUTH:
		if(!this._packet.isReadable) return;

		this._user.authenticate(this._packet, _.bind(function (err, out) {
			if(this._user.userAuthenticated) {
				this._state = state.CHANNEL;
				this._channel.setUserInfo(this._user.getUserInfo());
			}
			this._packet.trim();
			this._conn.write(out);
		}, this));
		break;
	case state.CHANNEL:
		if(!this._packet.isReadable) return;

		var packet = this._channel.demultiplex(this._packet, _.bind(function (err, out) {
			this._packet.trim();
			if(out) this._conn.write(out);
		}, this));

		if(packet && packet.disconnect_msg) return this._disconnect(this._packet.disconnect_msg);
		else if(packet && packet.error_msg) return this._disconnect(this._packet.error_msg);
		break;

	}
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
TLP.prototype.hello = function (softwareversion, comments) {
	var protocolversion = getProtocolVersion(softwareversion, comments);
	this._kex.setServerProtocolVersion(protocolversion);
	this._conn.write(protocolversion);
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
TLP.prototype._exchangeProtocolVersion = function (packet) {
	var chs = [];
	while(!packet.eob()) {
		var ch;
		if((ch = packet.readChar()) === '\n') {
			var protover = chs.join('');
			if(!protover.match(/^SSH\-2\.0\-./)) return this._disconnect('not supported version');
			this._kex.setClientProtocolVersion(new Buffer(protover, 'ascii'));
			return;
		} else
			chs.push(ch);
	}
	return this._disconnect('unterminated literal');
};

TLP.prototype._disconnect = function (msg) {
	console.log('disconnect with ' + msg);
	this._conn.destroy();
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
function getProtocolVersion (softwareversion, comments) {
	return new Buffer('SSH-2.0-' + softwareversion + (comments ? ' ' + comments : '') + '\r\n');
}<commit_msg>removing debug print<commit_after>var crypto = require('crypto'),
	_ = require('underscore');
var SSHPacket = require('./ssh-packet'),
	SSHKex = require('./ssh-kex'),
	SSHUserAuth = require('./ssh-userauth'),
	SSHChannel = require('./ssh-channel');

var state = {
	PROTOCOL_VERSION_EXCHANGE: 'PROTOCOL_VERSION_EXCHANGE',
	KEX: 'KEX',
	SERVICE_REQUEST: 'SERVICE_REQUEST',
	USER_AUTH: 'USER_AUTH',
	CHANNEL: 'CHANNEL'
};

function TLP (conn, al) {
	this._conn = conn;
	this._al = al;

	this._conn.on('data', _.bind(function (buf) {
		this._packet.append(buf);
		this._process();
	}, this));
	this._state = state.PROTOCOL_VERSION_EXCHANGE;

	this._packet = new SSHPacket.SSHPacket();
	this._kex = new SSHKex.SSHKex();
	this._user = new SSHUserAuth.SSHUserAuth();
	this._user.onUserAuth(_.bind(function (request, response) {
		this._al.emit('userauth', request, response);
	}, this));
	this._channel = new SSHChannel.SSHChannel();
	this._channel.onData(_.bind(function (request, response) {
		this._al.emit('data', request, response);
	}, this));
}

var MESSAGE_NUMBER = {
	SSH_MSG_SERVICE_REQUEST: 5, 			/* RFC 4253 SSH Transport Layer Protocol Section 12 */
	SSH_MSG_SERVICE_ACCEPT: 6, 				/* RFC 4253 SSH Transport Layer Protocol Section 12 */
};

module.exports = TLP;

TLP.prototype._process = function () {
	switch(this._state) {
	case state.PROTOCOL_VERSION_EXCHANGE:
		this._exchangeProtocolVersion(this._packet);
		this._state = state.KEX
		if(this._packet.eob()) break;
	case state.KEX:
		if(!this._packet.isReadable) return;

		this._kex.exchangeKeys(this._packet, _.bind(function (err, out) {
			if(this._kex.keyExchanged) {
				this._packet.attachKeyFunctions(
						this._kex.recvhashsize(), this._kex.recvblocksize(), this._kex.recvhmacf(), this._kex.decipherf(),
						this._kex.sendhashsize(), this._kex.sendblocksize(), this._kex.sendhmacf(), this._kex.cipherf());
				this._state = state.SERVICE_REQUEST;
			}

			this._packet.trim();
			this._conn.write(out);
		}, this));

		break;
	case state.SERVICE_REQUEST:
		if(!this._packet.isReadable) return;

		var payload = this._packet.readBinaryPacket();
		if(this._packet.disconnect_msg) return this._disconnect(this._packet.disconnect_msg);
		else if(this._packet.error_msg) return this._disconnect(this._packet.error_msg);
		else if(!payload) return;

		if(payload.readUInt8(0) === MESSAGE_NUMBER.SSH_MSG_SERVICE_REQUEST) {
			this._state = state.USER_AUTH;
			payload = payload.slice(1, payload.length);
			if(SSHPacket.readString(payload).string.toString('ascii') === 'ssh-userauth') {
				var user_auth = SSHPacket.createString(new Buffer('ssh-userauth', 'ascii'));
				this._conn.write(this._packet.createBinaryPacket(MESSAGE_NUMBER.SSH_MSG_SERVICE_ACCEPT, SSHPacket.createString(user_auth)));
			} else {
				throw new Error('unknown service requested: ' + payload);
			}
		} else
			throw new Error('unknown request: ' + payload.readUInt8(0));
		break;
	case state.USER_AUTH:
		if(!this._packet.isReadable) return;

		this._user.authenticate(this._packet, _.bind(function (err, out) {
			if(this._user.userAuthenticated) {
				this._state = state.CHANNEL;
				this._channel.setUserInfo(this._user.getUserInfo());
			}
			this._packet.trim();
			this._conn.write(out);
		}, this));
		break;
	case state.CHANNEL:
		if(!this._packet.isReadable) return;

		var packet = this._channel.demultiplex(this._packet, _.bind(function (err, out) {
			this._packet.trim();
			if(out) this._conn.write(out);
		}, this));

		if(packet && packet.disconnect_msg) return this._disconnect(this._packet.disconnect_msg);
		else if(packet && packet.error_msg) return this._disconnect(this._packet.error_msg);
		break;

	}
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
TLP.prototype.hello = function (softwareversion, comments) {
	var protocolversion = getProtocolVersion(softwareversion, comments);
	this._kex.setServerProtocolVersion(protocolversion);
	this._conn.write(protocolversion);
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
TLP.prototype._exchangeProtocolVersion = function (packet) {
	var chs = [];
	while(!packet.eob()) {
		var ch;
		if((ch = packet.readChar()) === '\n') {
			var protover = chs.join('');
			if(!protover.match(/^SSH\-2\.0\-./)) return this._disconnect('not supported version');
			this._kex.setClientProtocolVersion(new Buffer(protover, 'ascii'));
			return;
		} else
			chs.push(ch);
	}
	return this._disconnect('unterminated literal');
};

TLP.prototype._disconnect = function (msg) {
	console.log('disconnect with ' + msg);
	this._conn.destroy();
};

/**
 *		RFC 4253 SSH Transport Layer Protocol Section 4.2
 */
function getProtocolVersion (softwareversion, comments) {
	return new Buffer('SSH-2.0-' + softwareversion + (comments ? ' ' + comments : '') + '\r\n');
}<|endoftext|>