Another round of churn - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit d2b998dd5f188d4f7c037ae8953471eab9c1fa27
 (DIR) parent 7cf35f7ee06eae9797292dce6b5a566e7142d46b
 (HTM) Author: HD Moore <hd_moore@rapid7.com>
       Date:   Sun, 30 Dec 2012 17:32:57 -0600
       
       Another round of churn
       
       Diffstat:
         M Gemfile                             |       7 ++++++-
         M Gemfile.lock                        |      19 ++++++++++++++-----
         M app/assets/stylesheets/bootstrap_a… |     130 ++++++++++++++++++++++++++++---
         M app/controllers/application_contro… |      86 +++++++++++++++++--------------
         M app/controllers/dial_jobs_controll… |      11 ++++++++---
         M app/controllers/dial_results_contr… |       7 +++++--
         A app/controllers/projects_controlle… |      92 +++++++++++++++++++++++++++++++
         M app/controllers/providers_controll… |       7 +++++++
         A app/controllers/settings_controlle… |       2 ++
         A app/controllers/user_sessions_cont… |      25 +++++++++++++++++++++++++
         A app/controllers/users_controller.rb |      36 +++++++++++++++++++++++++++++++
         M app/helpers/application_helper.rb   |       4 ++++
         A app/helpers/projects_helper.rb      |       2 ++
         A app/helpers/settings_helper.rb      |       2 ++
         A app/helpers/user_sessions_helper.rb |       2 ++
         A app/helpers/users_helper.rb         |       2 ++
         M app/models/dial_job.rb              |      15 +++++++++++++++
         A app/models/project.rb               |       5 +++++
         M app/models/provider.rb              |       2 +-
         A app/models/settings.rb              |       3 +++
         A app/models/user.rb                  |       8 ++++++++
         A app/models/user_session.rb          |       3 +++
         D app/tabs/tabulous.rb                |     175 -------------------------------
         M app/views/dial_jobs/edit.html.erb   |       2 +-
         M app/views/dial_jobs/index.html.erb  |       6 +++---
         M app/views/dial_jobs/new.html.erb    |       2 +-
         M app/views/dial_results/edit.html.e… |       2 +-
         M app/views/dial_results/index.html.… |      12 ++++++------
         M app/views/dial_results/new.html.erb |       2 +-
         M app/views/dial_results/show.html.e… |       4 ++--
         M app/views/home/about.html.erb       |       8 ++++++++
         M app/views/layouts/application.html… |      60 ++++++++++++++++++++++---------
         A app/views/layouts/login.html.erb    |      41 +++++++++++++++++++++++++++++++
         A app/views/projects/edit.html.erb    |      11 +++++++++++
         A app/views/projects/index.html.erb   |      40 +++++++++++++++++++++++++++++++
         A app/views/projects/new.html.erb     |      18 ++++++++++++++++++
         A app/views/projects/show.html.erb    |      24 ++++++++++++++++++++++++
         A app/views/settings/index.html.erb   |       4 ++++
         A app/views/user_sessions/new.html.e… |      13 +++++++++++++
         A app/views/users/_form.html.erb      |       8 ++++++++
         A app/views/users/edit.html.erb       |       9 +++++++++
         A app/views/users/new.html.erb        |       7 +++++++
         A app/views/users/show.html.erb       |      37 +++++++++++++++++++++++++++++++
         A bin/adduser                         |      71 +++++++++++++++++++++++++++++++
         A bin/resetpw                         |      92 +++++++++++++++++++++++++++++++
         M config/application.rb               |       4 ++--
         M config/environment.rb               |       1 +
         M config/routes.rb                    |      51 +++++++++++++++++++++----------
         M db/migrate/20121228171549_initial_… |      54 ++++++++++++++++++++++++++++---
         M db/schema.rb                        |      42 +++++++++++++++++++++++++++++++
         D lib/templates/erb/scaffold/_form.h… |      11 -----------
         M lib/warvox/jobs.rb                  |      30 +++++++++++++++++++++++-------
       
       52 files changed, 1002 insertions(+), 309 deletions(-)
       ---
 (DIR) diff --git a/Gemfile b/Gemfile
       @@ -23,11 +23,16 @@ group :assets do
          gem 'uglifier', '>= 1.0.3'
        end
        
       +gem 'authlogic'
       +gem 'rails-settings-cached'
       +
        gem 'twitter-bootstrap-rails'
       +gem 'formtastic'
        gem 'formtastic-bootstrap'
       -gem 'tabulous'
       +gem 'rails_bootstrap_navbar'
        
        gem "therubyracer", :group => :assets, :platform => :ruby
        
        gem 'will_paginate', '~> 3.0'
       +gem 'will_paginate-bootstrap'
        gem 'dynamic_form'
 (DIR) diff --git a/Gemfile.lock b/Gemfile.lock
       @@ -37,6 +37,9 @@ GEM
              i18n (~> 0.6)
              multi_json (~> 1.0)
            arel (3.0.2)
       +    authlogic (3.1.0)
       +      activerecord (>= 3.0.7)
       +      activerecord (>= 3.0.7)
            builder (3.0.4)
            coffee-rails (3.2.2)
              coffee-script (>= 2.2.0)
       @@ -45,7 +48,6 @@ GEM
              coffee-script-source
              execjs
            coffee-script-source (1.4.0)
       -    colored (1.2)
            commonjs (0.2.6)
            daemons (1.1.9)
            dynamic_form (1.1.4)
       @@ -98,6 +100,10 @@ GEM
              activesupport (= 3.2.8)
              bundler (~> 1.0)
              railties (= 3.2.8)
       +    rails-settings-cached (0.2.4)
       +      rails (>= 3.0.0)
       +    rails_bootstrap_navbar (0.1.5.beta)
       +      rails (>= 3.0.0)
            railties (3.2.8)
              actionpack (= 3.2.8)
              activesupport (= 3.2.8)
       @@ -118,9 +124,6 @@ GEM
              hike (~> 1.2)
              rack (~> 1.0)
              tilt (~> 1.1, != 1.3.0)
       -    tabulous (1.3.0)
       -      colored (~> 1.2.0)
       -      rails (~> 3.0)
            therubyracer (0.11.0)
              ref
            thin (1.5.0)
       @@ -142,13 +145,17 @@ GEM
              execjs (>= 0.3.0)
              multi_json (~> 1.0, >= 1.0.2)
            will_paginate (3.0.3)
       +    will_paginate-bootstrap (0.2.2)
       +      will_paginate (>= 3.0.3)
        
        PLATFORMS
          ruby
        
        DEPENDENCIES
       +  authlogic
          coffee-rails (~> 3.2.1)
          dynamic_form
       +  formtastic
          formtastic-bootstrap
          jquery-datatables-rails
          jquery-rails
       @@ -157,10 +164,12 @@ DEPENDENCIES
          pg (= 0.11)
          postgres_ext!
          rails (= 3.2.8)
       +  rails-settings-cached
       +  rails_bootstrap_navbar
          sass-rails (~> 3.2.3)
       -  tabulous
          therubyracer
          thin
          twitter-bootstrap-rails
          uglifier (>= 1.0.3)
          will_paginate (~> 3.0)
       +  will_paginate-bootstrap
 (DIR) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less
       @@ -10,16 +10,6 @@ body {
        @iconSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings.png');
        @iconWhiteSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings-white.png');
        
       -// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
       -// Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not
       -// have the proper paths. So for now we use the absolute path.
       -@fontAwesomeEotPath: '/assets/fontawesome-webfont.eot';
       -@fontAwesomeWoffPath: '/assets/fontawesome-webfont.woff';
       -@fontAwesomeTtfPath: '/assets/fontawesome-webfont.ttf';
       -@fontAwesomeSvgPath: '/assets/fontawesome-webfont.svg';
       -
       -// Font Awesome
       -@import "fontawesome.less";
        
        @sansFontFamily: "Trebuchet MS", Arial, Helvetica, sans-serif;
        @green: #90d552;
       @@ -38,14 +28,130 @@ body {
        @navbarLinkColorActive: @white;
        @navbarLinkColorHover: @yellow;
        
       -@dropdownLinkColorHover: @yellow;
       -@dropdownLinkBackgroundHover: @dropdownBackground;
       +@dropdownLinkColorHover: @white;
       +@dropdownLinkBackgroundHover: @orange;
       +@dropdownBackgroundActive: @white;
       +@dropdownBackground: @white;
        
        @headingsColor: @darkGray;
        
        @navbarBackground: #ea5709;
        @navbarBackgroundHighlight: #4A1C04;
        
       +.project-title {
       +        font-size: 22px;
       +        color: @white;
       +        margin-top: 10px;
       +        margin-right: 50px;
       +
       +}
       +
       +// Hacks to override active drop-down item background color and hover
       +.dropdown-menu .active > a {
       +        background-color: @white;
       +        background-image: none;
       +        color: @darkGray;
       +}
       +
       +// Do not make active icons white
       +.dropdown-menu > .active > a > [class^="icon-"], .dropdown-menu > .active > a > [class*="icon-"] {
       +        color: @darkGray;
       +        background-image: url("/assets/twitter/bootstrap/glyphicons-halflings.png");
       +}
       +
       +.dropdown-menu .active > a:hover {
       +        background-color: @orange;
       +        background-image: none;
       +        color: @white;
       +}
       +
       +.fconstrained {
       +        width: 400px;
       +}
       +
       +.fbtn {
       +
       +}
       +
       +.project_description {
       +        height: 100px;
       +        padding: 5px;
       +}
       +.project_includes {
       +        height: 100px;
       +        padding: 5px;
       +}
       +
       +body#login {
       +
       +background-color: black;
       +padding: 0;
       +margin: 0;
       +
       +}
       +
       +#login-panel {
       +
       +margin: 0px;
       +width: 100%;
       +padding: 0;
       +height: 300px;
       +
       +position: fixed;
       +z-index: 1;
       +
       +/* IE10 Consumer Preview */
       +background-image: -ms-linear-gradient(top, #EA5709 0%, #000000 100%);
       +
       +/* Mozilla Firefox */
       +background-image: -moz-linear-gradient(top, #EA5709 0%, #000000 100%);
       +
       +/* Opera */
       +background-image: -o-linear-gradient(top, #EA5709 0%, #000000 100%);
       +
       +/* Webkit (Safari/Chrome 10) */
       +background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #EA5709), color-stop(1, #000000));
       +
       +/* Webkit (Chrome 11+) */
       +background-image: -webkit-linear-gradient(top, #EA5709 0%, #000000 100%);
       +
       +/* W3C Markup, IE10 Release Preview */
       +background-image: linear-gradient(to bottom, #EA5709 0%, #000000 100%);
       +
       +}
       +
       +#login-logo {
       +        margin-left: auto;
       +        margin-right: auto;
       +        width: 88px;
       +        height: 20px;
       +        margin-top: 60px;
       +}
       +
       +#box {
       +        margin-left: auto;
       +        margin-right: auto;
       +        width: 350px;
       +        background: white;
       +        padding: 25px;
       +        margin-top: 20px;
       +        border: 1px solid #4A1C04;
       +        margin-bottom: 30px;
       +}
       +
       +.login-label {
       +        width: 120px;
       +        margin-right: 10px;
       +        margin-bottom: 5px;
       +        text-align: right;
       +        font-weight: bold;
       +        float: left;
       +}
       +
       +.btn-login {
       +        margin-left: 130px;
       +}
       +
        .nav {
           a {
            font-size: 15px;
 (DIR) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
       @@ -1,52 +1,62 @@
        class ApplicationController < ActionController::Base
                helper :all
                protect_from_forgery
       -        before_filter :get_auth  
       +        helper_method :current_user_session, :current_user
       +        before_filter :require_user, :load_project
       +        add_breadcrumb :projects, :root_path
        
        private
        
       -        def get_creds
       -                username = nil
       -                password = nil
       -                
       -                headers = %W{X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION HTTP_AUTHORIZATION}
       -        
       -                headers.each do |head|
       -                        blob = request.env[head]
       -                        next if not blob
       -
       -                        meth,blob = blob.split(/\s+/)        
       -                        next if not blob
       -                        next if meth.downcase != 'basic'
       -                
       -                        username,password = blob.unpack('m*')[0].split(':', 2)
       -                        break if (username and username.length > 0)
       +        def current_user_session
       +                return @current_user_session if defined?(@current_user_session)
       +                @current_user_session = UserSession.find
       +        end
       +
       +        def current_user
       +                return @current_user if defined?(@current_user)
       +                @current_user = current_user_session && current_user_session.record
       +        end
       +
       +        def require_user
       +                unless current_user
       +                        store_location
       +                        flash[:notice] = "You must be logged in to access this page"
       +                        redirect_to '/login'
       +                        return false
                        end
       +        end
        
       -                [username, password]
       -        end 
       -
       -        def check_auth
       -                return true if session[:user]
       -                user,pass = get_creds
       -                return false if not (user and pass)
       -                
       -                if(WarVOX::Config.authenticate(user,pass))
       -                        session[:user] = user
       -                        return true
       +        def require_no_user
       +                if current_user
       +                        store_location
       +                        flash[:notice] = "You must be logged out to access this page"
       +                        redirect_to user_path(current_user)
       +                        return false
                        end
       -                
       -                return false
                end
       -        
       -        def get_auth                
       -                if(not check_auth())
       -                        response.headers["Status"] = "Unauthorized" 
       -                        response.headers["WWW-Authenticate"] = 'Basic realm="WarVOX Console"'
       -                        render :text => "Authentication Failure", :status => 401                          
       -                        return
       +
       +        def store_location
       +                session[:return_to] = request.fullpath
       +        end
       +
       +        def redirect_back_or_default(default)
       +                redirect_to(session[:return_to] || default)
       +                session[:return_to] = nil
       +        end
       +
       +        def load_project
       +                if params[:project_id]
       +                        @project = Project.find(params[:project_id])
       +                elsif session[:project_id]
       +                        @project = Project.find(session[:project_id])
       +                end
       +
       +                if @project and @project.id and not (session[:project_id] and session[:project_id] == @project.id)
       +                        session[:project_id] = @project
                        end
       +
                        true
                end
       -                
       +
       +
        end
 (DIR) diff --git a/app/controllers/dial_jobs_controller.rb b/app/controllers/dial_jobs_controller.rb
       @@ -38,10 +38,13 @@ class DialJobsController < ApplicationController
          def stop
            @dial_job = DialJob.find(params[:id])
        
       +        @dial_job.stop
       +
                if(@dial_job.status != 'submitted')
                  flash[:notice] = 'Job is already running or completed'
                  return
                end
       +    format.html { redirect_to :action => 'index' }
          end
        
        
       @@ -75,10 +78,12 @@ class DialJobsController < ApplicationController
              if @dial_job.save
                flash[:notice] = 'Job was successfully created.'
        
       -        # Launch it
       -        WarVOX::JobManager.schedule(::WarVOX::Jobs::Dialer, @dial_job.id)
       +                res = @dial_job.schedule(:dialer)
       +                unless res
       +                        flash[:error] = "Unable to launch dialer job"
       +                end
        
       -        format.html { redirect_to :action => 'index' }
       +            format.html { redirect_to :action => 'index' }
                format.xml  { render :xml => @dial_job, :status => :created, :location => @dial_job }
              else
                format.html { render :action => "new" }
 (DIR) diff --git a/app/controllers/dial_results_controller.rb b/app/controllers/dial_results_controller.rb
       @@ -64,8 +64,11 @@ class DialResultsController < ApplicationController
                        :conditions => [ 'completed = ? and processed = ? and busy = ?', true, false, false ]
                )
        
       -        if(@dial_data_todo.length > 0)
       -        WarVOX::JobManager.schedule(::WarVOX::Jobs::Analysis, @job_id)
       +        if @dial_data_todo.length > 0
       +        res = @job.schedule(:analysis)
       +                unless res
       +                        flash[:error] = "Unable to launch analysis job"
       +                end
                end
          end
        
 (DIR) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
       @@ -0,0 +1,92 @@
       +class ProjectsController < ApplicationController
       +
       +        def index
       +                   @projects = Project.paginate(
       +                        :page => params[:page],
       +                        :order => 'id DESC',
       +                        :per_page => 10
       +                )
       +
       +                @new_project = Project.new
       +
       +                respond_to do |format|
       +                        format.html # index.html.erb
       +                        format.xml  { render :xml => @projects }
       +                end
       +        end
       +
       +  # GET /projects/1
       +  # GET /projects/1.xml
       +  def show
       +    @project = Project.find(params[:id])
       +    respond_to do |format|
       +      format.html # show.html.erb
       +      format.xml  { render :xml => @project }
       +    end
       +  end
       +
       +  # GET /projects/new
       +  # GET /projects/new.xml
       +  def new
       +    @new_project = Project.new
       +
       +    respond_to do |format|
       +      format.html # new.html.erb
       +      format.xml  { render :xml => @new_project }
       +    end
       +  end
       +
       +  # GET /projects/1/edit
       +  def edit
       +    @project = Project.find(params[:id])
       +  end
       +
       +  # POST /projects
       +  # POST /projects.xml
       +  def create
       +    @project = Project.new(params[:project])
       +    @project.created_by = current_user.login
       +
       +    respond_to do |format|
       +      if @project.save
       +        flash[:notice] = 'Project was successfully created.'
       +        format.html { redirect_to(project_path(@project)) }
       +        format.xml  { render :xml => @project, :status => :created, :location => @project }
       +      else
       +        format.html { render :action => "new" }
       +        format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
       +      end
       +    end
       +  end
       +
       +  # PUT /projects/1
       +  # PUT /projects/1.xml
       +  def update
       +    @project = Project.find(params[:id])
       +
       +    respond_to do |format|
       +      if @project.update_attributes(params[:project])
       +        flash[:notice] = 'Project was successfully updated.'
       +        format.html { redirect_to projects_path }
       +        format.xml  { head :ok }
       +      else
       +        format.html { render :action => "edit" }
       +        format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
       +      end
       +    end
       +  end
       +
       +  # DELETE /projects/1
       +  # DELETE /projects/1.xml
       +  def destroy
       +    @project = Project.find(params[:id])
       +    @project.destroy
       +
       +    respond_to do |format|
       +      format.html { redirect_to(projects_url) }
       +      format.xml  { head :ok }
       +    end
       +  end
       +
       +
       +end
 (DIR) diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb
       @@ -17,6 +17,7 @@ class ProvidersController < ApplicationController
          # GET /providers/1.xml
          def show
            @provider = Provider.find(params[:id])
       +        @provider.pass = "********"
        
            respond_to do |format|
              format.html # show.html.erb
       @@ -39,6 +40,7 @@ class ProvidersController < ApplicationController
          # GET /providers/1/edit
          def edit
            @provider = Provider.find(params[:id])
       +        @provider.pass = "********"
          end
        
          # POST /providers
       @@ -64,6 +66,11 @@ class ProvidersController < ApplicationController
          def update
            @provider = Provider.find(params[:id])
        
       +        # Dont set the password if its the placeholder
       +        if params[:provider] and params[:provider][:pass] and params[:provider][:pass] == "********"
       +                params[:provider].delete(:pass)
       +        end
       +
            respond_to do |format|
              if @provider.update_attributes(params[:provider])
                flash[:notice] = 'Provider was successfully updated.'
 (DIR) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb
       @@ -0,0 +1,2 @@
       +class SettingsController < ApplicationController
       +end
 (DIR) diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb
       @@ -0,0 +1,25 @@
       +class UserSessionsController < ApplicationController
       +        before_filter :require_no_user, :only => [:new, :create]
       +        before_filter :require_user, :only => :destroy
       +        layout 'login'
       +
       +        def new
       +                @user_session = UserSession.new
       +        end
       +
       +        def create
       +                @user_session = UserSession.new(params[:user_session])
       +                if @user_session.save
       +                        flash[:notice] = "Login successful!"
       +                        redirect_back_or_default projects_path
       +                else
       +                        render :action => :new
       +                end
       +        end
       +
       +        def destroy
       +                current_user_session.destroy
       +                flash[:notice] = "Logout successful!"
       +                redirect_back_or_default login_url
       +        end
       +end
 (DIR) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
       @@ -0,0 +1,36 @@
       +class UsersController < ApplicationController
       +  before_filter :require_no_user, :only => [:new, :create]
       +  before_filter :require_user, :only => [:show, :edit, :update]
       +
       +  def new
       +    @user = User.new
       +  end
       +
       +  def create
       +    @user = User.new(params[:user])
       +    if @user.save
       +      flash[:notice] = "Account registered!"
       +      redirect_back_or_default user_path(@user)
       +    else
       +      render :action => :new
       +    end
       +  end
       +
       +  def show
       +    @user = @current_user
       +  end
       +
       +  def edit
       +    @user = @current_user
       +  end
       +
       +  def update
       +    @user = @current_user # makes our views "cleaner" and more consistent
       +    if @user.update_attributes(params[:user])
       +      flash[:notice] = "Account updated!"
       +      redirect_to account_url
       +    else
       +      render :action => :edit
       +    end
       +  end
       +end
 (DIR) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
       @@ -17,4 +17,8 @@ module ApplicationHelper
                  _html << %{</select>}
                  raw(_html)
                end
       +
       +        def set_focus(element_id)
       +                javascript_tag(" $elem = $(\"#{element_id}\"); if (null !== $elem && $elem.length > 0){$elem.focus()}")
       +        end
        end
 (DIR) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
       @@ -0,0 +1,2 @@
       +module ProjectsHelper
       +end
 (DIR) diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
       @@ -0,0 +1,2 @@
       +module SettingsHelper
       +end
 (DIR) diff --git a/app/helpers/user_sessions_helper.rb b/app/helpers/user_sessions_helper.rb
       @@ -0,0 +1,2 @@
       +module UserSessionsHelper
       +end
 (DIR) diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
       @@ -0,0 +1,2 @@
       +module UsersHelper
       +end
 (DIR) diff --git a/app/models/dial_job.rb b/app/models/dial_job.rb
       @@ -31,4 +31,19 @@ class DialJob < ActiveRecord::Base
                end
        
                attr_accessible :range, :seconds, :lines, :cid_mask
       +
       +
       +        def schedule(jtype)
       +                res = nil
       +                case jtype
       +                when :dialer
       +                        res = WarVOX::JobManager.schedule(::WarVOX::Jobs::Dialer, self[:id])
       +                when :analysis
       +                        res = WarVOX::JobManager.schedule(::WarVOX::Jobs::Analysis, self[:id])
       +                else
       +                        raise RuntimeError, "Unknown task type: #{jtype} for Job #{self[:id]}"
       +                end
       +                res
       +        end
       +
        end
 (DIR) diff --git a/app/models/project.rb b/app/models/project.rb
       @@ -0,0 +1,5 @@
       +class Project < ActiveRecord::Base
       +        validates_presence_of :name
       +
       +        attr_accessible :name, :description, :included, :excluded
       +end
 (DIR) diff --git a/app/models/provider.rb b/app/models/provider.rb
       @@ -5,5 +5,5 @@ class Provider < ActiveRecord::Base
                validates_numericality_of :port, :less_than => 65536, :greater_than => 0
                validates_numericality_of :lines, :less_than => 255, :greater_than => 0
        
       -        attr_accessible :name, :host, :port, :user, :pass, :lines
       +        attr_accessible :enabled, :name, :host, :port, :user, :pass, :lines
        end
 (DIR) diff --git a/app/models/settings.rb b/app/models/settings.rb
       @@ -0,0 +1,3 @@
       +class Settings < RailsSettings::CachedSettings
       +        attr_accessible :var
       +end
 (DIR) diff --git a/app/models/user.rb b/app/models/user.rb
       @@ -0,0 +1,8 @@
       +class User < ActiveRecord::Base
       +        include RailsSettings::Extend
       +        acts_as_authentic do |c|
       +                c.validate_email_field = false
       +                c.merge_validates_length_of_password_field_options :minimum => 8
       +                c.merge_validates_length_of_password_confirmation_field_options :minimum => 8
       +        end
       +end
 (DIR) diff --git a/app/models/user_session.rb b/app/models/user_session.rb
       @@ -0,0 +1,3 @@
       +class UserSession < Authlogic::Session::Base
       +        logout_on_timeout true
       +end
 (DIR) diff --git a/app/tabs/tabulous.rb b/app/tabs/tabulous.rb
       @@ -1,175 +0,0 @@
       -# Tabulous gives you an easy way to set up tabs for your Rails application.
       -#
       -#   1. Configure this file.
       -#   2. Add <%= tabs %> and <%= subtabs %> in your layout(s) wherever you want
       -#      your tabs to appear.
       -#   3. Add styles for these tabs in your stylesheets.
       -#   4. Profit!
       -
       -Tabulous.setup do |config|
       -
       -  #---------------------------
       -  #   HOW TO USE THE TABLES
       -  #---------------------------
       -  #
       -  # The following tables are just an array of arrays.  As such, you can put
       -  # any Ruby code into a cell.  For example, you could put "/foo/bar" in
       -  # a path cell or you could put "/foo" + "/bar".  You can even wrap up code
       -  # in a lambda to be executed later.  These will be executed in the context
       -  # of a Rails view meaning they will have access to view helpers.
       -  #
       -  # However, there is something special about the format of these tables.
       -  # Because it would be a pain for you to manually prettify the tables each
       -  # time you edit them, there is a special rake task that does this for
       -  # you: rake tabs:format.  However, for this prettifier to work properly
       -  # you have to follow some special rules:
       -  #
       -  #   * No comments are allowed between rows.
       -  #   * Comments are allowed to the right of rows, except for header rows.
       -  #   * The start of a table is signified by a [ all by itself on a line.
       -  #   * The end of a table is signified by a ] all by itself on a line.
       -  #   * And most importantly: commas that separate cells should be surrounded
       -  #     by spaces and commas that are within cells should not.  This gives the
       -  #     formatter an easy way to distinguish between cells without having
       -  #     to actually parse the Ruby.
       -
       -  #----------
       -  #   TABS
       -  #----------
       -  #
       -  # This is where you define your tabs and subtabs.  The order that the tabs
       -  # appear in this list is the order they will appear in your views.  Any
       -  # subtabs defined will have the previous tab as their parent.
       -  #
       -  # TAB NAME
       -  #   must end in _tab or _subtab
       -  # DISPLAY TEXT
       -  #   the text the user sees on the tab
       -  # PATH
       -  #   the URL that gets sent to the server when the tab is clicked
       -  # VISIBLE
       -  #   whether to display the tab
       -  # ENABLED
       -  #   whether the tab is disabled (unclickable)
       -
       -  config.tabs do
       -    [
       -      #----------------------------------------------------------------------------------------------------------#
       -      #    TAB NAME             |    DISPLAY TEXT    |    PATH                 |    VISIBLE?    |    ENABLED?    #
       -      #----------------------------------------------------------------------------------------------------------#
       -      [    :start_tab           ,    'Start'         ,    new_dial_job_path    ,    true        ,    true        ],
       -      [    :dial_jobs_tab       ,    'Jobs'          ,    dial_jobs_path       ,    true        ,    true        ],
       -      [    :dial_results_tab    ,    'Results'       ,    dial_results_path    ,    true        ,    true        ],
       -      [    :providers_tab       ,    'Providers'     ,    providers_path       ,    true        ,    true        ],
       -      [    :about_tab           ,    'About'         ,    about_path           ,    true        ,    true        ],
       -      #----------------------------------------------------------------------------------------------------------#
       -      #    TAB NAME             |    DISPLAY TEXT    |    PATH                 |    VISIBLE?    |    ENABLED?    #
       -      #----------------------------------------------------------------------------------------------------------#
       -    ]
       -  end
       -
       -  #-------------
       -  #   ACTIONS
       -  #-------------
       -  #
       -  # This is where you hook up actions with tabs.  That way tabulous knows
       -  # which tab and subtab to mark active when an action is rendered.
       -  #
       -  # CONTROLLER
       -  #   the name of the controller
       -  # ACTION
       -  #   the name of the action, or :all_actions
       -  # TAB
       -  #   the name of the tab or subtab that is active when this action is rendered
       -
       -  config.actions do
       -    [
       -      #--------------------------------------------------------------------#
       -      #    CONTROLLER       |    ACTION          |    TAB                  #
       -      #--------------------------------------------------------------------#
       -      [    :dial_jobs       ,    :new            ,    :start_tab           ],
       -      [    :dial_jobs       ,    :index          ,    :dial_jobs_tab       ],
       -      [    :dial_results    ,    :all_actions    ,    :dial_results_tab    ],
       -      [    :analyze         ,    :all_actions    ,    :dial_results_tab    ],
       -      [    :providers       ,    :all_actions    ,    :providers_tab       ],
       -      [    :home            ,    :about          ,    :about_tab           ],
       -      #--------------------------------------------------------------------#
       -      #    CONTROLLER       |    ACTION          |    TAB                  #
       -      #--------------------------------------------------------------------#
       -    ]
       -  end
       -
       -  #---------------------
       -  #   GENERAL OPTIONS
       -  #---------------------
       -
       -  # By default, you cannot click on the active tab.
       -  config.active_tab_clickable = true
       -
       -  # By default, the subtabs HTML element is not rendered if it is empty.
       -  config.always_render_subtabs = false
       -
       -  # Tabulous expects every controller action to be associated with a tab.
       -  # When an action does not have an associated tab (or subtab), you can
       -  # instruct tabulous how to behave:
       -  #config.when_action_has_no_tab = :raise_error      # the default behavior
       -  #config.when_action_has_no_tab = :do_not_render  # no tab navigation HTML will be generated
       -
       -  config.when_action_has_no_tab = :render         # the tab navigation HTML will be generated,
       -                                                    # but no tab or subtab will be active
       -
       -  #--------------------
       -  #   MARKUP OPTIONS
       -  #--------------------
       -
       -  # By default, div elements are used in the tab markup.  When html5 is
       -  # true, nav elements are used instead.
       -  config.html5 = false
       -
       -  # This gives you control over what class the <ul> element that wraps the tabs
       -  # will have.  Good for interfacing with third-party code like Twitter
       -  # Bootstrap.
       -  config.tabs_ul_class = "nav"
       -
       -  # This gives you control over what class the <ul> element that wraps the subtabs
       -  # will have.  Good for interfacing with third-party code.
       -  config.subtabs_ul_class = "nav"
       -
       -  # Set this to true to have subtabs rendered in markup that Twitter Bootstrap
       -  # understands.  If this is set to true, you don't need to call subtabs in
       -  # your layout, just tabs.
       -  config.bootstrap_style_subtabs = true
       -
       -
       -  #-------------------
       -  #   STYLE OPTIONS
       -  #-------------------
       -  #
       -  # The markup that is generated has the following properties:
       -  #
       -  #   Tabs and subtabs that are selected have the class "active".
       -  #   Tabs and subtabs that are not selected have the class "inactive".
       -  #   Tabs that are disabled have the class "disabled"; otherwise, "enabled".
       -  #   Tabs that are not visible do not appear in the markup at all.
       -  #
       -  # These classes are provided to make it easier for you to create your
       -  # own CSS (and JavaScript) for the tabs.
       -
       -  # Some styles will be generated for you to get you off to a good start.
       -  # Scaffolded styles are not meant to be used in production as they
       -  # generate invalid HTML markup.  They are merely meant to give you a
       -  # head start or an easy way to prototype quickly.  Set this to false if
       -  # you are using Twitter Bootstrap.
       -  #
       -  config.css.scaffolding = false
       -
       -  # You can tweak the colors of the generated CSS.
       -
       -  config.css.background_color = '#000'
       -  config.css.text_color = '#444'
       -  config.css.active_tab_color = '#000'
       -  config.css.hover_tab_color = '#000'
       -  config.css.inactive_tab_color = '#000'
       -  config.css.inactive_text_color = '#000'
       -
       -end
 (DIR) diff --git a/app/views/dial_jobs/edit.html.erb b/app/views/dial_jobs/edit.html.erb
       @@ -21,4 +21,4 @@
        <% end %>
        
        <%= link_to 'Show', @dial_job %> |
       -<%= link_to 'Back', dial_jobs_path %>
       +<%= link_to 'Back', dial_jobs_path(@project) %>
 (DIR) diff --git a/app/views/dial_jobs/index.html.erb b/app/views/dial_jobs/index.html.erb
       @@ -23,7 +23,7 @@
            <td><%=h dial_job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
        
            <td>
       -            <a class="btn btn-mini" href="<%= run_dial_job_path(dial_job) %>" data-confirm="Launch this job?" rel="nofollow tooltip" title="Launch Job"><i class="icon-play"></i></a>
       +            <a class="btn btn-mini" href="<%= run_dial_job_path(@project, dial_job) %>" data-confirm="Launch this job?" rel="nofollow tooltip" title="Launch Job"><i class="icon-play"></i></a>
                        <a class="btn btn-mini" href="<%= dial_job %>" data-confirm="Remove this job?" data-method="delete" rel="nofollow tooltip" title="Remove Job"><i class="icon-trash"></i></a>
                </td>
          </tr>
       @@ -60,7 +60,7 @@
            <td><%=h dial_job.progress %>%</td>
            <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
            <td>
       -                <a class="btn btn-mini" href="<%= stop_dial_job(dial_job) %>" data-confirm="Terminate this job?" rel="nofollow tooltip" title="Terminate Job"><i class="icon-stop"></i></a>
       +                <a class="btn btn-mini" href="<%= stop_dial_job_path(@project, dial_job) %>" data-confirm="Terminate this job?" rel="nofollow tooltip" title="Terminate Job"><i class="icon-stop"></i></a>
                </td>
          </tr>
        <% end %>
       @@ -76,6 +76,6 @@
        <% if (@active_jobs.length + @submitted_jobs.length == 0) %>
        <h1 class='title'>No Active Jobs</h1>
        <br/>
       -<a class="btn" href="<%= new_dial_job_path %>"><i class="icon-plus"></i> Start Job </a>
       +<a class="btn" href="<%= new_dial_job_path(@project) %>"><i class="icon-plus"></i> Start Job </a>
        
        <% end %>
 (DIR) diff --git a/app/views/dial_jobs/new.html.erb b/app/views/dial_jobs/new.html.erb
       @@ -32,4 +32,4 @@
          </p>
        <% end %>
        
       -<%= link_to 'Back', dial_jobs_path %>
       +<%= link_to 'Back', dial_jobs_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/edit.html.erb b/app/views/dial_results/edit.html.erb
       @@ -45,4 +45,4 @@
        <% end %>
        
        <%= link_to 'Show', @dial_result %> |
       -<%= link_to 'Back', dial_results_path %>
       +<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/index.html.erb b/app/views/dial_results/index.html.erb
       @@ -28,16 +28,16 @@
            <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
        
            <td>
       -        <a class="btn btn-mini" href="<%= view_dial_result_path(dial_job) %>" rel="tooltip" title="View Call Connections" ><i class="icon-bar-chart"></i></a>
       +        <a class="btn btn-mini" href="<%= view_dial_result_path(@project,dial_job) %>" rel="tooltip" title="View Call Connections" ><i class="icon-bar-chart"></i></a>
        
                        <% if(dial_job.processed) %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(dial_job) %>" rel="tooltip" title="View Call Analysis"><i class="icon-eye-open"></i></a>
       -                        <a class="btn btn-mini" href="<%= reanalyze_dial_result_path(dial_job) %>" data-confirm="Reprocess this job?" rel="nofollow tooltip" title="Rerun Call Analysis"><i class="icon-refresh"></i></a>
       +                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(@project,dial_job) %>" rel="tooltip" title="View Call Analysis"><i class="icon-eye-open"></i></a>
       +                        <a class="btn btn-mini" href="<%= reanalyze_dial_result_path(@project,dial_job) %>" data-confirm="Reprocess this job?" rel="nofollow tooltip" title="Rerun Call Analysis"><i class="icon-refresh"></i></a>
                        <% else %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(dial_job) %>" data-confirm="Analyze this job?" rel="nofollow tooltip" title="Run Call Analysis"><i class="icon-bolt"></i></a>
       +                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(@project,dial_job) %>" data-confirm="Analyze this job?" rel="nofollow tooltip" title="Run Call Analysis"><i class="icon-bolt"></i></a>
                        <% end %>
        
       -            <a class="btn btn-mini" href="<%= dial_result_path(dial_job) %>" data-confirm="Delete all data for this job?" data-method="delete" rel="nofollow tooltip" title="Delete Call Data"><i class="icon-trash"></i></a>
       +            <a class="btn btn-mini" href="<%= dial_result_path(@project,dial_job) %>" data-confirm="Delete all data for this job?" data-method="delete" rel="nofollow tooltip" title="Delete Call Data"><i class="icon-trash"></i></a>
                </td>
          </tr>
        
       @@ -54,4 +54,4 @@
        
        <% end %>
        
       -<a class="btn" href="<%= new_dial_job_path %>"><i class="icon-plus"></i> Start Job </a>
       +<a class="btn" href="<%= new_dial_job_path(@project) %>"><i class="icon-plus"></i> Start Job </a>
 (DIR) diff --git a/app/views/dial_results/new.html.erb b/app/views/dial_results/new.html.erb
       @@ -40,4 +40,4 @@
          </p>
        <% end %>
        
       -<%= link_to 'Back', dial_results_path %>
       +<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/show.html.erb b/app/views/dial_results/show.html.erb
       @@ -44,5 +44,5 @@
        </p>
        
        
       -<%= link_to 'Edit', edit_dial_result_path(@dial_result) %> |
       -<%= link_to 'Back', dial_results_path %>
       +<%= link_to 'Edit', edit_dial_result_path(@project, @dial_result) %> |
       +<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/home/about.html.erb b/app/views/home/about.html.erb
       @@ -23,6 +23,14 @@ and research purposes only. The latest version of WarVOX can be found in <i clas
                <td><%= WarVOX::VERSION %></td>
        </tr>
        
       +
       +<tr>
       +        <td>
       +                Projects
       +        </td>
       +        <td><%= Project.count %></td>
       +</tr>
       +
        <tr>
                <td>
                        Providers
 (DIR) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
       @@ -14,35 +14,62 @@
            <%= javascript_include_tag "application" %>
            <%= stylesheet_link_tag "application", :media => "all" %>
        
       -    <%= favicon_link_tag 'images/apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144' %>
       -    <%= favicon_link_tag 'images/apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114' %>
       -    <%= favicon_link_tag 'images/apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72' %>
       -    <%= favicon_link_tag 'images/apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png' %>
       -    <%= favicon_link_tag 'images/favicon.ico', :rel => 'shortcut icon' %>
       +    <%= favicon_link_tag '/assets/apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144' %>
       +    <%= favicon_link_tag '/assets/apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114' %>
       +    <%= favicon_link_tag '/assets/apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72' %>
       +    <%= favicon_link_tag '/assets/apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png' %>
       +    <%= favicon_link_tag '/assets/favicon.ico', :rel => 'shortcut icon' %>
        
                <%= javascript_tag do %>
                        $(document).ready(function() {
                                $("a").tooltip();
       -                }
       +                });
                <% end %>
          </head>
          <body>
       +        <div class="container">
       +                <%= nav_bar :fixed => :top, :brand => raw('<img src="/assets/logo_light.png" border=0 alt="WarVOX">'), :responsive => true do %>
        
       -    <div class="navbar navbar-fixed-top">
       -      <div class="navbar-inner">
       -        <div class="container">
       -          <a class="brand" href="/"><img src="/assets/logo_light.png" border=0 alt="WarVOX"></a>
       -          <%= tabs %>
       -        </div>
       -      </div>
       -    </div>
       +                        <%= menu_group :pull => :right  do %>
       +                        <% if @project and @project.id %>
       +                                <li class="project-title"><%= truncate(@project.name, :length => 25) %></li>
       +                                <%= menu_item "Dial", new_dial_job_path(@project) %>
       +                                <%= menu_item "Jobs", dial_jobs_path(@project) %>
       +                                <%= menu_item "Results", dial_results_path(@project) %>
       +                                <%= menu_item "Analysis", analyze_path(@project)%>
       +                        <% end %>
       +
       +                                <%= drop_down "Projects" do %>
       +                                        <%= menu_item raw('<i class="icon-list"></i> Browse Projects'),projects_path %>
       +                                        <%= menu_item raw('<i class="icon-plus"></i> Create Project'), new_project_path %>
       +                                        <%= drop_down_divider %>
       +                                        <%= drop_down_header "Recent Projects" %>
       +                                        <% Project.find(:all, :order => 'ID DESC', :limit => 5).each do |project| %>
       +                                                <%= menu_item raw('<i class="icon-chevron-right"></i> ' + truncate(project.name, :length => 15).html_safe),project_path(project) %>
       +                                        <% end %>
       +                                <% end %>
       +
       +                                <%= drop_down "Administration" do %>
       +                                        <%= menu_item raw('<i class="icon-user"></i> My Account'), user_path(current_user) %>
       +                                        <%= menu_item raw('<i class="icon-globe"></i> Providers'), providers_path %>
       +                                        <%= menu_item raw('<i class="icon-wrench"></i> Config'), settings_path %>
       +                                        <%= menu_item raw('<i class="icon-info-sign"></i> About'), about_path %>
       +                                <% end %>
       +
       +                                <%= menu_item "Logout", logout_path %>
       +                        <% end %>
       +                <% end %>
        
        
       -    <div class="container">
              <div class="row">
                <div class="span12 content">
       -
       +<% # render_breadcrumbs %>
                  <div class="content">
       +
       +
       +
       +<p style="color: green"><%= flash[:notice] %></p>
       +
                  <%= yield %>
                  </div>
        
       @@ -54,6 +81,5 @@
              </div>
            </div> <!-- /container -->
        
       -
          </body>
        </html>
 (DIR) diff --git a/app/views/layouts/login.html.erb b/app/views/layouts/login.html.erb
       @@ -0,0 +1,41 @@
       +<!DOCTYPE html>
       +<html lang="en">
       +  <head>
       +    <meta charset="utf-8">
       +    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
       +    <meta name="viewport" content="width=device-width, initial-scale=1.0">
       +    <title><%= content_for?(:title) ? yield(:title) : "WarVOX v#{WarVOX::VERSION}" %></title>
       +    <%= csrf_meta_tags %>
       +
       +    <!--[if lt IE 9]>
       +        <%= javascript_include_tag "html5" %>
       +    <![endif]-->
       +
       +    <%= javascript_include_tag "application" %>
       +    <%= stylesheet_link_tag "application", :media => "all" %>
       +
       +    <%= favicon_link_tag 'images/apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144' %>
       +    <%= favicon_link_tag 'images/apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114' %>
       +    <%= favicon_link_tag 'images/apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72' %>
       +    <%= favicon_link_tag 'images/apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png' %>
       +    <%= favicon_link_tag 'images/favicon.ico', :rel => 'shortcut icon' %>
       +
       +        <%= javascript_tag do %>
       +                $(document).ready(function() {
       +                        $("a").tooltip();
       +                });
       +        <% end %>
       +  </head>
       +  <body id="login">
       +  <div id="login-panel">
       +  <div id="login-logo"><img src="/assets/logo_light.png" border=0 alt="WarVOX"></div>
       +  <div id="box">
       +    <%= yield %>
       +  </div>
       +
       +    <footer class="footer">
       +      <p>WarVOX v<%=WarVOX::VERSION %> &copy; Rapid7, Inc. 2009-2013</p>
       +    </footer>
       +
       +  </body>
       +</html>
 (DIR) diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb
       @@ -0,0 +1,11 @@
       +<h1 class='title'>Update Project</h1>
       +
       +<%= semantic_form_for(@project) do |f| %>
       +        <%= f.input :name, :as => :string, :label => 'Project Name' %>
       +        <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %>
       +        <%= f.input :included,  :as => :text, :label => 'Default phone numbers to include', :input_html => { :class => 'project_includes' } %>
       +        <%= f.input :excluded,  :as => :text, :label => 'Default phone numbers to exclude', :input_html => { :class => 'project_includes' } %>
       +        <%= f.actions :submit, :label => 'Update' %>
       +<% end %>
       +
       +<%= link_to 'Back', projects_path %>
 (DIR) diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
       @@ -0,0 +1,40 @@
       +<% if @projects.length > 0 %>
       +<h1 class='title'>WarVOX Projects</h1>
       +
       +<%= will_paginate @projects, :renderer => BootstrapPagination::Rails %>
       +<table class='table table-striped ' width='90%'>
       +  <thead>
       +  <tr>
       +    <th>Name</th>
       +    <th>Description</th>
       +    <th>Date</th>
       +    <th>Actions</th>
       +  </tr>
       +  </thead>
       +  <tbody>
       +
       +<% @projects.sort{|a,b| b.id <=> a.id}.each do |project|  %>
       +  <tr>
       +    <td><%= link_to( h(project.name), project_path(project)) %></td>
       +    <td><%=truncate(project.description, :length => 40, :separator => '') %></td>
       +    <td><%=h project.updated_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
       +    <td>
       +              <a class="btn btn-mini" href="<%= edit_project_path(project) %>"rel="tooltip" title="Update Project Information"><i class="icon-pencil"></i></a>
       +            <a class="btn btn-mini" href="<%= project_path(project) %>" data-confirm="Delete this project?" data-method="delete" rel="nofollow tooltip" title="Delete Project"><i class="icon-trash"></i></a>
       +        </td>
       +  </tr>
       +
       +<% end %>
       +</tbody>
       +</table>
       +
       +<%= will_paginate @projects, :renderer => BootstrapPagination::Rails %>
       +
       +<% else %>
       +
       +<h1 class='title'>No Projects</h1>
       +<br/>
       +
       +<% end %>
       +
       +<a class="btn" href="<%= new_project_path %>"><i class="icon-plus"></i> Create Project </a>
 (DIR) diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb
       @@ -0,0 +1,18 @@
       +<h1 class='title'>New Project</h1>
       +
       +<div class='fconstrained'>
       +
       +<%= semantic_form_for(@new_project) do |f| %>
       +        <%= f.input :name, :as => :string, :label => 'Name' %>
       +        <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %>
       +        <%= f.input :included,  :as => :text, :label => 'Default phone numbers to include', :input_html => { :class => 'project_includes' } %>
       +        <%= f.input :excluded,  :as => :text, :label => 'Default phone numbers to exclude', :input_html => { :class => 'project_includes' } %>
       +
       +        <%= f.action :submit, :label => 'Create', :button_html => { :class => 'btn btn-large fbtn' }  %>
       +
       +        <a class="btn btn-link" href="<%= projects_path %>"rel="tooltip" title="Return to projects"><i class="icon-return"></i>Cancel</a>
       +<% end %>
       +</div>
       +
       +
       +<%= set_focus('project_name') %>
 (DIR) diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
       @@ -0,0 +1,24 @@
       +<h1 class='title'>View Project</h1>
       +<p>
       +  <b>Name:</b>
       +  <%=h @project.name %>
       +</p>
       +
       +<p>
       +  <b>Description:</b>
       +  <%=h @project.description %>
       +</p>
       +
       +<p>
       +  <b>Default Include:</b>
       +  <%=h @project.included %>
       +</p>
       +
       +<p>
       +  <b>Default Exclude:</b>
       +  <%=h @project.excluded %>
       +</p>
       +
       +
       +<%= link_to 'Edit', edit_project_path(@project) %> |
       +<%= link_to 'Back', projects_path %>
 (DIR) diff --git a/app/views/settings/index.html.erb b/app/views/settings/index.html.erb
       @@ -0,0 +1,4 @@
       +<% Settings.all.each do |s| %>
       +<%= s.inspect %><br/>
       +
       +<% end %>
 (DIR) diff --git a/app/views/user_sessions/new.html.erb b/app/views/user_sessions/new.html.erb
       @@ -0,0 +1,13 @@
       +        <noscript>
       +                <p class="noscript_warning">Please enable Javascript before using WarVOX</p>
       +        </noscript>
       +
       +        <%= form_for @user_session do |f| %>
       +                <% if @user_session.errors.any? %>
       +                        <div class="alert"><%= @user_session.errors.full_messages.first %></div>
       +                <% end %>
       +                <p><div class="login-label"><h3>Username</h3></div><%= f.text_field :login, :spellcheck => false %></p>
       +                <p><div class="login-label"><h3>Password</h3></div><%= f.password_field :password, :autocomplete => 'off' %></p>
       +                <%= f.submit "Sign in", :class => "btn-login btn btn-warning" %>
       +        <% end %>
       +        <%= set_focus('user_session_login') %>
 (DIR) diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb
       @@ -0,0 +1,8 @@
       +<%= form.label :login %><br />
       +<%= form.text_field :login %><br />
       +<br />
       +<%= form.label :password, form.object.new_record? ? nil : "Change password" %><br />
       +<%= form.password_field :password %><br />
       +<br />
       +<%= form.label :password_confirmation %><br />
       +<%= form.password_field :password_confirmation %><br />
 (DIR) diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb
       @@ -0,0 +1,9 @@
       +<h1>Edit My Account</h1>
       +
       +<% form_for @user, :url => user_path(@user) do |f| %>
       +  <%= f.error_messages %>
       +  <%= render :partial => "form", :object => f %>
       +  <%= f.submit "Update" %>
       +<% end %>
       +
       +<br /><%= link_to "My Profile", user_path(@user) %>
 (DIR) diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb
       @@ -0,0 +1,7 @@
       +<h1>Register</h1>
       +
       +<% form_for @user, :url => account_path do |f| %>
       +  <%= f.error_messages %>
       +  <%= render :partial => "form", :object => f %>
       +  <%= f.submit "Register" %>
       +<% end %>
 (DIR) diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
       @@ -0,0 +1,37 @@
       +<p>
       +  <b>Login:</b>
       +  <%=h @user.login %>
       +</p>
       +
       +<p>
       +  <b>Login count:</b>
       +  <%=h @user.login_count %>
       +</p>
       +
       +<p>
       +  <b>Last request at:</b>
       +  <%=h @user.last_request_at %>
       +</p>
       +
       +<p>
       +  <b>Last login at:</b>
       +  <%=h @user.last_login_at %>
       +</p>
       +
       +<p>
       +  <b>Current login at:</b>
       +  <%=h @user.current_login_at %>
       +</p>
       +
       +<p>
       +  <b>Last login ip:</b>
       +  <%=h @user.last_login_ip %>
       +</p>
       +
       +<p>
       +  <b>Current login ip:</b>
       +  <%=h @user.current_login_ip %>
       +</p>
       +
       +
       +<%= link_to 'Edit', edit_user_path %>
 (DIR) diff --git a/bin/adduser b/bin/adduser
       @@ -0,0 +1,71 @@
       +#!/usr/bin/env ruby
       +
       +ENV['RAILS_ENV'] ||= 'production'
       +
       +# bundler/setup just sets up the $LOAD_PATHs, the gems aren't automatically required...
       +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile',File.dirname(__FILE__))
       +require 'bundler/setup'
       +
       +# rails/all must be required explicitly to get the railties that pro/ui/config/application.rb uses
       +require 'rails/all'
       +# require all the gems in the current environment
       +Bundler.require(*Rails.groups(:assets => %w(development test cucumber)))
       +
       +APP_PATH = File.expand_path('../../config/application',  __FILE__)
       +require File.expand_path('../../config/boot',  __FILE__)
       +require APP_PATH
       +Rails.application.require_environment!
       +
       +def generate_password
       +        set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*")
       +        set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
       +        str = ''
       +        cnt = 0
       +        while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
       +                if str.length > 12
       +                        str = str[0,4]
       +                        next
       +                end
       +                str << set[ rand(set.length), 1]
       +                cnt += 1
       +        end
       +        str
       +end
       +
       +
       +username = ARGV.shift
       +password = ARGV.shift
       +
       +user = username ? User.find_by_login(username) : nil
       +
       +if not user
       +
       +           if ! username
       +                $stdout.write "[*] Please enter a username: "
       +                $stdout.flush
       +                username = $stdin.readline.strip
       +        end
       +
       +        if ! (username and username.strip.length > 0)
       +                $stdout.puts "[-] Invalid username specified"
       +                exit(0)
       +        end
       +
       +        if not password
       +                randpass = generate_password
       +                $stdout.puts ""
       +                $stdout.puts "[*] Creating user '#{username}' with password '#{randpass}' ..."
       +                $stdout.puts ""
       +                password = randpass
       +        end
       +
       +        user = User.new()
       +        user.login = username
       +        user.password = password
       +        user.password_confirmation = password
       +        user.save!
       +
       +        $stdout.puts "[*] User #{user.login} has been created, please change your password on login."
       +else
       +        $stdout.puts "[*] That user account already exists, please try 'resetpw' script"
       +end
 (DIR) diff --git a/bin/resetpw b/bin/resetpw
       @@ -0,0 +1,92 @@
       +#!/usr/bin/env ruby
       +
       +ENV['RAILS_ENV'] ||= 'production'
       +
       +# bundler/setup just sets up the $LOAD_PATHs, the gems aren't automatically required...
       +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile',File.dirname(__FILE__))
       +require 'bundler/setup'
       +
       +# rails/all must be required explicitly to get the railties that pro/ui/config/application.rb uses
       +require 'rails/all'
       +# require all the gems in the current environment
       +Bundler.require(*Rails.groups(:assets => %w(development test cucumber)))
       +
       +APP_PATH = File.expand_path('../../config/application',  __FILE__)
       +require File.expand_path('../../config/boot',  __FILE__)
       +require APP_PATH
       +Rails.application.require_environment!
       +
       +uname = ARGV.shift
       +upass = ARGV.shift
       +
       +def generate_password
       +        set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*")
       +        set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
       +        str = ''
       +        cnt = 0
       +        while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
       +                if str.length > 12
       +                        str = str[0,4]
       +                        next
       +                end
       +                str << set[ rand(set.length), 1]
       +                cnt += 1
       +        end
       +        str
       +end
       +
       +
       +user = uname ? User.find_by_login(uname) : User.find(:first)
       +if uname and not user
       +        $stderr.puts "[-] User #{uname} was not found"
       +        exit(1)
       +end
       +
       +if not user
       +        $stderr.puts "[-] No user account has been created"
       +        exit(1)
       +end
       +
       +randpass = upass || generate_password()
       +
       +
       +$stdout.puts %Q|
       +
       +********************************
       +*                              *
       +*    WarVOX Password Reset     *
       +*                              *
       +********************************
       +
       +[*] Warning! This tool will reset the password for the '#{user.login}' user account.
       +[*] To continue, please type "yes"
       +
       +|
       +
       +$stdout.write "Continue? (yes/no) > "
       +$stdout.flush
       +
       +inp = $stdin.readline
       +
       +if inp.strip.downcase != 'yes'
       +        $stdout.puts "[*] Reset cancelled, hit enter to exit"
       +        $stdin.readline
       +        exit(0)
       +end
       +
       +
       +user.password = randpass
       +user.password_confirmation = randpass
       +user.save!
       +
       +$stdout.puts %Q|
       +[*] The password for #{user.login} has been reset to a random value
       +
       +    New Password: #{randpass}
       +
       +[*] Please change this password on the next login.
       +|
       +
       +$stdout.puts "[*] Hit enter to exit"
       +$stdin.readline
       +exit(0)
 (DIR) diff --git a/config/application.rb b/config/application.rb
       @@ -40,7 +40,7 @@ module Web
            config.encoding = "utf-8"
        
            # Configure sensitive parameters which will be filtered from the log file.
       -    config.filter_parameters += [:password, :pass]
       +    config.filter_parameters += [:password, :pass, :password, :password_confirmation]
        
            # Enable escaping HTML in JSON.
            config.active_support.escape_html_entities_in_json = true
       @@ -64,7 +64,7 @@ module Web
        
            # Configure sensitive parameters which will be filtered from the log file.
                config.filter_parameters += [:password, :pass]
       -    
       +
                config.session_store :cookie_store, :key => "_warvox"
                config.secret_token = WarVOX::Config.load_session_key
          end
 (DIR) diff --git a/config/environment.rb b/config/environment.rb
       @@ -3,3 +3,4 @@ require File.expand_path('../application', __FILE__)
        
        # Initialize the rails application
        Web::Application.initialize!
       +
 (DIR) diff --git a/config/routes.rb b/config/routes.rb
       @@ -1,24 +1,43 @@
        Web::Application.routes.draw do
        
       -  resources :dial_jobs
       -  resources :dial_results
       +  resources :projects
       +  resources :settings
          resources :providers
        
       -  match '/dial_jobs/:id/run'         => 'dial_jobs#run', :as => :run_dial_job
       -  match '/dial_results/:id/view'     => 'dial_results#view', :as => :view_dial_result
       -  match '/dial_results/:id/analyze'  => 'dial_results#analyze', :as => :analyze_dial_result
       -  match '/dial_results/:id/reanalyze'  => 'dial_results#reanalyze', :as => :reanalyze_dial_result      
       -  match '/dial_results/:id/purge'    => 'dial_results#purge', :as => :purge_dial_result    
       -
       -  match '/analyze/:id/resource/:result_id/:type' => 'analyze#resource', :as => :resource_analyze
       -  match '/analyze/:id/view'    => 'analyze#view', :as => :view_analyze
       -  match '/analyze/:dial_result_id/matches'    => 'analyze#view_matches', :as => :view_matches
       -  match '/analyze/:id/show'    => 'analyze#show', :as => :show_analyze  
       -  match '/analyze'             => 'analyze#index'
       -    
       +  resources :users
       +  match "login" => "user_sessions#new", :as => "login"
       +  match "logout" => "user_sessions#destroy", :as => "logout"
       +  resources :user_sessions
       +
       +  match  '/projects/:project_id/all'                    => 'projects#index', :as => :all_projects
       +
       +  match  '/projects/:project_id/jobs'                   => 'dial_jobs#index', :as => :dial_jobs
       +  match  '/projects/:project_id/jobs/:id/run'           => 'dial_jobs#run', :as => :run_dial_job
       +  match  '/projects/:project_id/jobs/:id/stop'          => 'dial_jobs#stop', :as => :stop_dial_job
       +  match  '/projects/:project_id/jobs/new'               => 'dial_jobs#new', :as => :new_dial_job
       +  delete '/projects/:project_id/jobs/:id'               => 'dial_jobs#destroy'
       +
       +  match  '/projects/:project_id/results/'               => 'dial_results#index', :as => :dial_results
       +  match  '/projects/:project_id/results/:id/view'       => 'dial_results#view', :as => :view_dial_result
       +  match  '/projects/:project_id/results/:id/analyze'    => 'dial_results#analyze', :as => :analyze_dial_result
       +  match  '/projects/:project_id/results/:id/reanalyze'  => 'dial_results#reanalyze', :as => :reanalyze_dial_result
       +  match  '/projects/:project_id/results/:id/purge'      => 'dial_results#purge', :as => :purge_dial_result
       +  delete '/projects/:project_id/results/:id'            => 'dial_results#destroy'
       +
       +  match '/projects/:project_id/analyze'             => 'analyze#index', :as => :analyze
       +  match '/projects/:project_id/analyze/:id/resource/:result_id/:type' => 'analyze#resource', :as => :resource_analyze
       +  match '/projects/:project_id/analyze/:id/view'    => 'analyze#view', :as => :view_analyze
       +  match '/projects/:project_id/analyze/:dial_result_id/matches'    => 'analyze#view_matches', :as => :view_matches
       +  match '/projects/:project_id/analyze/:id/show'    => 'analyze#show', :as => :show_analyze
       +
       +  match '/projects/:project_id/providers'           => 'providers#index', :as => :project_providers
       +
       +
       +  match '/projects/:project_id/about'    => 'home#about', :as => :project_about
       +
       +  match '/projects/:project_id/settings'    => 'settings#index', :as => :project_settings
          match '/about'               => 'home#about'
          match '/home/about'          => 'home#about'
       -  
       -  root :to => "home#index"
        
       +  root :to => "projects#index"
        end
 (DIR) diff --git a/db/migrate/20121228171549_initial_schema.rb b/db/migrate/20121228171549_initial_schema.rb
       @@ -4,6 +4,49 @@ class InitialSchema < ActiveRecord::Migration
                        # Require the intarray extension
                        execute("CREATE EXTENSION IF NOT EXISTS intarray")
        
       +
       +                create_table :settings do |t|
       +                        t.string :var, :null => false
       +                        t.text   :value, :null => true
       +                        t.integer :thing_id, :null => true
       +                        t.string :thing_type, :limit => 30, :null => true
       +                        t.timestamps
       +                end
       +
       +                add_index :settings, [ :thing_type, :thing_id, :var ], :unique => true
       +
       +                create_table 'users' do |t|
       +                        t.string    :login,               :null => false                # optional, you can use email instead, or both
       +                        t.string    :email,               :null => true                 # optional, you can use login instead, or both
       +                        t.string    :crypted_password,    :null => false                # optional, see below
       +                        t.string    :password_salt,       :null => false                # optional, but highly recommended
       +                        t.string    :persistence_token,   :null => false                # required
       +                        t.string    :single_access_token, :null => false                # optional, see Authlogic::Session::Params
       +                        t.string    :perishable_token,    :null => false                # optional, see Authlogic::Session::Perishability
       +
       +                        # Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present.
       +                        t.integer   :login_count,         :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
       +                        t.integer   :failed_login_count,  :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
       +                        t.datetime  :last_request_at                                    # optional, see Authlogic::Session::MagicColumns
       +                        t.datetime  :current_login_at                                   # optional, see Authlogic::Session::MagicColumns
       +                        t.datetime  :last_login_at                                      # optional, see Authlogic::Session::MagicColumns
       +                        t.string    :current_login_ip                                   # optional, see Authlogic::Session::MagicColumns
       +                        t.string    :last_login_ip                                      # optional, see Authlogic::Session::MagicColumns
       +
       +                        t.timestamps
       +                        t.boolean   "enabled", :default => true
       +                        t.boolean   "admin",   :default => true
       +                end
       +
       +                create_table 'projects' do |t|
       +                        t.timestamps
       +                        t.text      "name"
       +                        t.text      "description"
       +                        t.text                "included"
       +                        t.text                "excluded"
       +                        t.string        "created_by"
       +                end
       +
                        create_table "dial_jobs" do |t|
                                t.timestamps
                                t.text                "range"
       @@ -63,9 +106,12 @@ class InitialSchema < ActiveRecord::Migration
                end
        
                def down
       -                remove_table "providers"
       -                remove_table "dial_result_media"
       -                remove_table "dial_results"
       -                remove_table "dial_jobs"
       +                drop_table "providers"
       +                drop_table "dial_result_media"
       +                drop_table "dial_results"
       +                drop_table "dial_jobs"
       +                drop_table "projects"
       +                drop_table "users"
       +                drop_table "settings"
                end
        end
 (DIR) diff --git a/db/schema.rb b/db/schema.rb
       @@ -62,6 +62,16 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
            t.integer  "fprint",                         :array => true
          end
        
       +  create_table "projects", :force => true do |t|
       +    t.datetime "created_at",  :null => false
       +    t.datetime "updated_at",  :null => false
       +    t.text     "name"
       +    t.text     "description"
       +    t.text     "included"
       +    t.text     "excluded"
       +    t.string   "created_by"
       +  end
       +
          create_table "providers", :force => true do |t|
            t.datetime "created_at", :null => false
            t.datetime "updated_at", :null => false
       @@ -74,4 +84,36 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
            t.boolean  "enabled"
          end
        
       +  create_table "settings", :force => true do |t|
       +    t.string   "var",                      :null => false
       +    t.text     "value"
       +    t.integer  "thing_id"
       +    t.string   "thing_type", :limit => 30
       +    t.datetime "created_at",               :null => false
       +    t.datetime "updated_at",               :null => false
       +  end
       +
       +  add_index "settings", ["thing_type", "thing_id", "var"], :name => "index_settings_on_thing_type_and_thing_id_and_var", :unique => true
       +
       +  create_table "users", :force => true do |t|
       +    t.string   "login",                                 :null => false
       +    t.string   "email"
       +    t.string   "crypted_password",                      :null => false
       +    t.string   "password_salt",                         :null => false
       +    t.string   "persistence_token",                     :null => false
       +    t.string   "single_access_token",                   :null => false
       +    t.string   "perishable_token",                      :null => false
       +    t.integer  "login_count",         :default => 0,    :null => false
       +    t.integer  "failed_login_count",  :default => 0,    :null => false
       +    t.datetime "last_request_at"
       +    t.datetime "current_login_at"
       +    t.datetime "last_login_at"
       +    t.string   "current_login_ip"
       +    t.string   "last_login_ip"
       +    t.datetime "created_at",                            :null => false
       +    t.datetime "updated_at",                            :null => false
       +    t.boolean  "enabled",             :default => true
       +    t.boolean  "admin",               :default => true
       +  end
       +
        end
 (DIR) diff --git a/lib/templates/erb/scaffold/_form.html.erb b/lib/templates/erb/scaffold/_form.html.erb
       @@ -1,11 +0,0 @@
       -<%%= semantic_form_for @<%= singular_name %> do |f| %>
       -  <%%= f.inputs do %>
       -  <%- attributes.each do |attribute| -%>
       -    <%%= f.input :<%= attribute.name %> %>
       -  <%- end -%>
       -  <%% end %>
       -
       -  <%%= f.actions do %>
       -    <%%= f.action :submit, :as => :input %>
       -  <%% end %>
       -<%% end %>
 (DIR) diff --git a/lib/warvox/jobs.rb b/lib/warvox/jobs.rb
       @@ -3,15 +3,15 @@ class JobQueue
                attr_accessor :active_job, :active_thread, :queue, :queue_thread
        
                require "thread"
       -        
       +
                def initialize
       -                @mutex = ::Mutex.new        
       +                @mutex = ::Mutex.new
                        @queue = []
                        @queue_thread = Thread.new{ manage_queue }
       -                
       +
                        super
                end
       -        
       +
                def scheduled?(klass, job_id)
                        @mutex.synchronize do
                                [@active_job, *(@queue)].each do |c|
       @@ -21,7 +21,7 @@ class JobQueue
                        end
                        false
                end
       -        
       +
                def schedule(klass, job_id)
                        begin
                        return false if scheduled?(klass, job_id)
       @@ -31,7 +31,23 @@ class JobQueue
                                false
                        end
                end
       -        
       +
       +        def stop(job_id)
       +                @mutex.synchronize do
       +                        [@active_job, *(@queue)].each do |c|
       +                                next if not c
       +                                if c.name == job_id
       +                                        # Actively running
       +                                        if @active_job == c
       +
       +                                        else
       +
       +                                        end
       +                                end
       +                        end
       +                end
       +        end
       +
                def manage_queue
                        begin
                        while(true)
       @@ -53,7 +69,7 @@ class JobQueue
                                $stderr.puts "QUEUE MANAGER:#{$!.class} #{$!}"
                                $stderr.flush
                        end
       -        end        
       +        end
        
        end
        end