| Commit some of the last minute changes for the WarVOX release - warvox - VoIP b… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 76fc65ae7c8b0a944971cfcdbdf4dae3bb1cd3ec | |
| parent 4c5bff4f8d4cde61e0435e1a96b92fc707b4cd38 | |
| Author: HD Moore <[email protected]> | |
| Date: Wed, 4 Mar 2009 00:15:40 +0000 | |
| Commit some of the last minute changes for the WarVOX release | |
| Diffstat: | |
| M Makefile | 5 ++++- | |
| A bin/verify_install.rb | 70 +++++++++++++++++++++++++++++… | |
| M lib/warvox/config.rb | 27 ++++++++++++++++++++++++++- | |
| M lib/warvox/jobs/analysis.rb | 11 ++++++++++- | |
| M lib/warvox/jobs/dialer.rb | 13 ++++++++++--- | |
| M web/app/controllers/dial_jobs_cont… | 13 ++++++++++++- | |
| M web/app/controllers/providers_cont… | 4 ++++ | |
| M web/app/models/dial_job.rb | 12 ++++++++++++ | |
| M web/app/models/provider.rb | 2 +- | |
| M web/app/views/analyze/index.html.e… | 2 ++ | |
| M web/app/views/analyze/view.html.erb | 10 ++++------ | |
| M web/app/views/dial_jobs/index.html… | 8 ++++++++ | |
| M web/app/views/dial_jobs/new.html.e… | 4 ++++ | |
| M web/app/views/dial_results/analyze… | 2 ++ | |
| M web/app/views/dial_results/edit.ht… | 4 ++++ | |
| M web/app/views/dial_results/index.h… | 2 ++ | |
| M web/app/views/dial_results/show.ht… | 5 +++++ | |
| M web/app/views/dial_results/view.ht… | 2 ++ | |
| M web/app/views/home/about.html.erb | 1 - | |
| M web/app/views/home/index.html.erb | 11 ++++++++--- | |
| M web/app/views/layouts/warvox.html.… | 6 +++--- | |
| M web/app/views/providers/edit.html.… | 5 ++++- | |
| M web/app/views/providers/index.html… | 3 ++- | |
| M web/app/views/providers/new.html.e… | 1 - | |
| M web/app/views/providers/show.html.… | 4 ++++ | |
| M web/config/environment.rb | 5 ++++- | |
| A web/db/migrate/20090303204859_add_… | 9 +++++++++ | |
| A web/db/migrate/20090303204917_add_… | 9 +++++++++ | |
| A web/db/migrate/20090303225838_add_… | 9 +++++++++ | |
| M web/db/schema.rb | 5 ++++- | |
| A web/public/images/close.gif | 0 | |
| A web/public/images/loading.gif | 0 | |
| A web/public/images/overlay.png | 0 | |
| A web/public/javascripts/lightbox.js | 426 +++++++++++++++++++++++++++++… | |
| A web/public/stylesheets/lightbox.css | 27 +++++++++++++++++++++++++++ | |
| 35 files changed, 691 insertions(+), 26 deletions(-) | |
| --- | |
| diff --git a/Makefile b/Makefile | |
| @@ -1,5 +1,8 @@ | |
| -all: install | |
| +all: test | |
| +test: install | |
| + bin/verify_install.rb | |
| + | |
| install: iaxrecord ruby-kissfft db | |
| cp -a src/iaxrecord/iaxrecord bin/ | |
| diff --git a/bin/verify_install.rb b/bin/verify_install.rb | |
| @@ -0,0 +1,70 @@ | |
| +#!/usr/bin/env ruby | |
| +################### | |
| + | |
| +# | |
| +# Load the library path | |
| +# | |
| +base = __FILE__ | |
| +while File.symlink?(base) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| +end | |
| +$:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| +require 'warvox' | |
| + | |
| +# | |
| +# Verify that WarVOX has been installed properly | |
| +# | |
| + | |
| +puts("**********************************************************************") | |
| +puts("* *") | |
| +puts("* WarVOX Installation Verifier *") | |
| +puts("* *") | |
| +puts("**********************************************************************") | |
| +puts(" ") | |
| + | |
| +begin | |
| + require 'kissfft' | |
| + puts "[*] The KissFFT module appears to be available" | |
| +rescue ::LoadError | |
| + puts "[*] ERROR: The KissFFT module has not been installed" | |
| + exit | |
| +end | |
| + | |
| +sox_path = WarVOX::Config.tool_path('sox') | |
| +if(not sox_path) | |
| + puts "[*] ERROR: The 'sox' binary could not be found" | |
| + exit | |
| +end | |
| + | |
| +sox_data = `#{sox_path} --help 2>&1` | |
| +if(sox_data !~ /SUPPORTED FILE FORMATS.*raw/) | |
| + puts "[*] ERROR: The 'sox' binary does not have support for RAW audio" | |
| + exit | |
| +end | |
| +puts "[*] The SOX binary appears to be available with RAW file support" | |
| + | |
| + | |
| +if(not WarVOX::Config.tool_path('gnuplot')) | |
| + puts "[*] ERROR: The 'gnuplot' binary could not be installed" | |
| + exit | |
| +end | |
| +puts "[*] The GNUPlot binary appears to be available" | |
| + | |
| +if(not WarVOX::Config.tool_path('lame')) | |
| + puts "[*] ERROR: The 'lame' binary could not be installed" | |
| + exit | |
| +end | |
| +puts "[*] The LAME binary appears to be available" | |
| + | |
| + | |
| +if(not WarVOX::Config.tool_path('iaxrecord')) | |
| + puts "[*] ERROR: The 'iaxrecord' binary could not be installed" | |
| + exit | |
| +end | |
| +puts "[*] The IAXRECORD binary appears to be available" | |
| + | |
| + | |
| +puts " " | |
| +puts "[*] Congratulations! You are now ready to run WarVOX" | |
| +puts "[*] Start WarVOX with bin/warvox.rb" | |
| +puts " " | |
| diff --git a/lib/warvox/config.rb b/lib/warvox/config.rb | |
| @@ -62,7 +62,32 @@ module Config | |
| } | |
| end | |
| return nil | |
| - end | |
| + end | |
| + | |
| + # This method prevents two installations of WarVOX from using the same | |
| + # rails session key. The first time this method is called, it generates | |
| + # a new key and stores it in the rails directory, afterwards this key | |
| + # will be used every time. | |
| + def self.load_session_key | |
| + kfile = File.join(WarVOX::Base, 'web', 'config', 'session.key') | |
| + if(not File.exists?(kfile)) | |
| + # XXX: assume /dev/urandom exists | |
| + kdata = File.read('/dev/urandom', 64).unpack("H*")[0] | |
| + | |
| + # Create the new session key file | |
| + fd = File.new(kfile, 'w') | |
| + | |
| + # Make this file mode 0600 | |
| + File.chmod(0600, kfile) | |
| + | |
| + # Write it and close | |
| + fd.write(kdata) | |
| + fd.close | |
| + return kdata | |
| + end | |
| + File.read(kfile) | |
| + end | |
| + | |
| end | |
| end | |
| diff --git a/lib/warvox/jobs/analysis.rb b/lib/warvox/jobs/analysis.rb | |
| @@ -3,7 +3,13 @@ module Jobs | |
| class Analysis < Base | |
| require 'fileutils' | |
| - require 'kissfft' | |
| + | |
| + @@kissfft_loaded = false | |
| + begin | |
| + require 'kissfft' | |
| + @kissfft_loaded = true | |
| + rescue ::LoadError | |
| + end | |
| def type | |
| 'analysis' | |
| @@ -11,6 +17,9 @@ class Analysis < Base | |
| def initialize(job_id) | |
| @name = job_id | |
| + if(not @@kissfft_loaded) | |
| + raise RuntimeError, "The KissFFT module is not availab… | |
| + end | |
| end | |
| def get_job | |
| diff --git a/lib/warvox/jobs/dialer.rb b/lib/warvox/jobs/dialer.rb | |
| @@ -15,7 +15,12 @@ class Dialer < Base | |
| @seconds = model.seconds | |
| @lines = model.lines | |
| @nums = shuffle_a(WarVOX::Phone.crack_mask(@range)) | |
| - @cid = '8005551212' # XXX: Read from job | |
| + | |
| + # CallerID modes (SELF or a mask) | |
| + @cid_self = model.cid_mask == 'SELF' | |
| + if(not @cid_self) | |
| + @cid_range = WarVOX::Phone.crack_mask(model.cid_mask) | |
| + end | |
| end | |
| # | |
| @@ -39,7 +44,7 @@ class Dialer < Base | |
| def get_providers | |
| res = [] | |
| - ::Provider.find(:all).each do |prov| | |
| + ::Provider.find_all_by_enabled(true).each do |prov| | |
| info = { | |
| :name => prov.name, | |
| :id => prov.id, | |
| @@ -116,6 +121,7 @@ class Dialer < Base | |
| fail = 1 | |
| byte = 0 | |
| path = '' | |
| + cid = @cid_self ? num : @cid_range[ r… | |
| IO.popen( | |
| [ | |
| @@ -123,7 +129,7 @@ class Dialer < Base | |
| prov[:host], | |
| prov[:user], | |
| prov[:pass], | |
| - @cid, | |
| + cid, | |
| out, | |
| num, | |
| @seconds | |
| @@ -144,6 +150,7 @@ class Dialer < Base | |
| res = ::DialResult.new | |
| res.number = num | |
| + res.cid = cid | |
| res.dial_job_id = @name | |
| res.provider_id = prov[:id] | |
| res.completed = (fail == 0) ? true : f… | |
| diff --git a/web/app/controllers/dial_jobs_controller.rb b/web/app/controllers/… | |
| @@ -55,13 +55,24 @@ class DialJobsController < ApplicationController | |
| # POST /dial_jobs | |
| # POST /dial_jobs.xml | |
| def create | |
| - @dial_job = DialJob.new(params[:dial_job]) | |
| + | |
| + @dial_job = DialJob.new(params[:dial_job]) | |
| + | |
| + if(Provider.find_all_by_enabled(true).length == 0) | |
| + @dial_job.errors.add("No providers have been configured or ena… | |
| + respond_to do |format| | |
| + format.html { render :action => "new" } | |
| + format.xml { render :xml => @dial_job.errors, :status… | |
| + end | |
| + return | |
| + end | |
| @dial_job.status = 'submitted' | |
| @dial_job.progress = 0 | |
| @dial_job.started_at = nil | |
| @dial_job.completed_at = nil | |
| @dial_job.range.gsub!(/[^0-9X]/, '') | |
| + @dial_job.cid_mask.gsub!(/[^0-9X]/, '') if @dial_job.cid_mask != "SELF" | |
| respond_to do |format| | |
| if @dial_job.save | |
| diff --git a/web/app/controllers/providers_controller.rb b/web/app/controllers/… | |
| @@ -6,6 +6,8 @@ class ProvidersController < ApplicationController | |
| def index | |
| @providers = Provider.find(:all) | |
| @new_provider = Provider.new | |
| + @new_provider.enabled = true | |
| + | |
| respond_to do |format| | |
| format.html # index.html.erb | |
| format.xml { render :xml => @providers } | |
| @@ -27,6 +29,7 @@ class ProvidersController < ApplicationController | |
| # GET /providers/new.xml | |
| def new | |
| @provider = Provider.new | |
| + @provider.enabled = true | |
| respond_to do |format| | |
| format.html # new.html.erb | |
| @@ -43,6 +46,7 @@ class ProvidersController < ApplicationController | |
| # POST /providers.xml | |
| def create | |
| @provider = Provider.new(params[:provider]) | |
| + @provider.enabled = true | |
| respond_to do |format| | |
| if @provider.save | |
| diff --git a/web/app/models/dial_job.rb b/web/app/models/dial_job.rb | |
| @@ -9,5 +9,17 @@ class DialJob < ActiveRecord::Base | |
| if(range.gsub(/[^0-9X]/, '').length != 10) | |
| errors.add(:range, "The range must be exactly 10 chara… | |
| end | |
| + | |
| + if(range.scan(/X/).length > 5) | |
| + errors.add(:range, "The range must contain no more tha… | |
| + end | |
| + | |
| + if(cid_mask != "SELF" and cid_mask.gsub(/[^0-9X]/, '').length … | |
| + errors.add(:range, "The Caller ID must be exactly 10 c… | |
| + end | |
| + | |
| + if(cid_mask != "SELF" and cid_mask.scan(/X/).length > 5) | |
| + errors.add(:range, "The Caller ID must contain no more… | |
| + end | |
| end | |
| end | |
| diff --git a/web/app/models/provider.rb b/web/app/models/provider.rb | |
| @@ -3,5 +3,5 @@ class Provider < ActiveRecord::Base | |
| validates_presence_of :name, :host, :port, :user, :pass, :lines | |
| validates_numericality_of :port, :less_than => 65536, :greater_than =>… | |
| - validates_numericality_of :lines, :less_than => 255, :greater_than => 1 | |
| + validates_numericality_of :lines, :less_than => 255, :greater_than => 0 | |
| end | |
| diff --git a/web/app/views/analyze/index.html.erb b/web/app/views/analyze/index… | |
| @@ -6,6 +6,7 @@ | |
| <tr> | |
| <th>ID</th> | |
| <th>Range</th> | |
| + <th>CallerID</th> | |
| <th>Connected</th> | |
| <th>Date</th> | |
| </tr> | |
| @@ -14,6 +15,7 @@ | |
| <tr> | |
| <td><%=h dial_job.id %></td> | |
| <td><%=h dial_job.range %></td> | |
| + <td><%=h dial_job.cid_mask %></td> | |
| <td><%=h DialResult.find(:all, :conditions => ['dial_job_id = ? and proces… | |
| <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>… | |
| <td><%= link_to 'View', view_analyze_path(dial_job) %></td> | |
| diff --git a/web/app/views/analyze/view.html.erb b/web/app/views/analyze/view.h… | |
| @@ -15,6 +15,7 @@ | |
| <th>Signal</th> | |
| <th>Spectrum</th> | |
| <th>Number</th> | |
| + <th>CallerID</th> | |
| <th>Provider</th> | |
| <th>Call Time</th> | |
| <th>Ring Time</th> | |
| @@ -24,18 +25,15 @@ | |
| <tr> | |
| <td><%=h dial_result.id %></td> | |
| <td> | |
| - <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia… | |
| - <img src="<%=resource_analyze_path(@job_id)%>?result_id=<%= di… | |
| - </a> | |
| + <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia… | |
| </td> | |
| <td> | |
| - <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia… | |
| - <img src="<%=resource_analyze_path(@job_id)%>?result_id=<%= di… | |
| - </a> | |
| + <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia… | |
| </td> | |
| <td> | |
| <a href="<%=resource_analyze_path(@job_id)%>?result_id=<%= dia… | |
| </td> | |
| + <td><%=h dial_result.cid %></td> | |
| <td><%=h dial_result.provider.name %></td> | |
| <td><%=h dial_result.seconds %></td> | |
| <td><%=h dial_result.ringtime %></td> | |
| diff --git a/web/app/views/dial_jobs/index.html.erb b/web/app/views/dial_jobs/i… | |
| @@ -6,6 +6,7 @@ | |
| <tr> | |
| <th>ID</th> | |
| <th>Range</th> | |
| + <th>CallerID</th> | |
| <th>Seconds</th> | |
| <th>Lines</th> | |
| <th>Submitted Time</th> | |
| @@ -15,6 +16,7 @@ | |
| <tr> | |
| <td><%=h dial_job.id %></td> | |
| <td><%=h dial_job.range %></td> | |
| + <td><%=h dial_job.cid_mask %></td> | |
| <td><%=h dial_job.seconds %></td> | |
| <td><%=h dial_job.lines %></td> | |
| <td><%=h dial_job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>… | |
| @@ -35,6 +37,7 @@ | |
| <tr> | |
| <th>ID</th> | |
| <th>Range</th> | |
| + <th>CallerID</th> | |
| <th>Seconds</th> | |
| <th>Lines</th> | |
| <th>Status</th> | |
| @@ -46,6 +49,7 @@ | |
| <tr class='active_job_row'> | |
| <td><%=h dial_job.id %></td> | |
| <td><%=h dial_job.range %></td> | |
| + <td><%=h dial_job.cid_mask %></td> | |
| <td><%=h dial_job.seconds %></td> | |
| <td><%=h dial_job.lines %></td> | |
| <td><%=h dial_job.status %></td> | |
| @@ -85,6 +89,10 @@ | |
| <%= f.text_field :lines, :value => 10 %> | |
| </p> | |
| <p> | |
| + <%= f.label :lines, 'The source Caller ID range (555-555-55XX)' %><br /> | |
| + <%= f.text_field :cid_mask, :value => '000-000-XXXX' %> | |
| + </p> | |
| + <p> | |
| <%= f.submit "Create" %> | |
| </p> | |
| <% end %> | |
| diff --git a/web/app/views/dial_jobs/new.html.erb b/web/app/views/dial_jobs/new… | |
| @@ -15,6 +15,10 @@ | |
| <%= f.text_field :lines, :value => 10 %> | |
| </p> | |
| <p> | |
| + <%= f.label :lines, 'The source Caller ID range (555-555-55XX or SELF)' %>… | |
| + <%= f.text_field :cid_mask, :value => '000-000-XXXX' %> | |
| + </p> | |
| + <p> | |
| <%= f.submit "Create" %> | |
| </p> | |
| <% end %> | |
| diff --git a/web/app/views/dial_results/analyze.html.rb b/web/app/views/dial_re… | |
| @@ -12,6 +12,7 @@ | |
| <table class='table_scaffold' width='100%'> | |
| <tr> | |
| <th>Number</th> | |
| + <th>CallerID</th> | |
| <th>Provider</th> | |
| <th>Call Time</th> | |
| <th>Ring Time</th> | |
| @@ -20,6 +21,7 @@ | |
| <% for dial_result in @dial_data_todo.sort{|a,b| a.number <=> b.number } %> | |
| <tr> | |
| <td><%=h dial_result.number %></td> | |
| + <td><%=h dial_result.cid %></td> | |
| <td><%=h dial_result.provider.name %></td> | |
| <td><%=h dial_result.seconds %></td> | |
| <td><%=h dial_result.ringtime %></td> | |
| diff --git a/web/app/views/dial_results/edit.html.erb b/web/app/views/dial_resu… | |
| @@ -8,6 +8,10 @@ | |
| <%= f.text_field :number %> | |
| </p> | |
| <p> | |
| + <%= f.label :cid %><br /> | |
| + <%= f.text_field :cid %> | |
| + </p> | |
| + <p> | |
| <%= f.label :dial_job_id %><br /> | |
| <%= f.text_field :dial_job_id %> | |
| </p> | |
| diff --git a/web/app/views/dial_results/index.html.erb b/web/app/views/dial_res… | |
| @@ -5,6 +5,7 @@ | |
| <tr> | |
| <th>ID</th> | |
| <th>Range</th> | |
| + <th>CallerID</th> | |
| <th>Seconds</th> | |
| <th>Lines</th> | |
| <th>Started</th> | |
| @@ -14,6 +15,7 @@ | |
| <tr> | |
| <td><%=h dial_job.id %></td> | |
| <td><%=h dial_job.range %></td> | |
| + <td><%=h dial_job.cid_mask %></td> | |
| <td><%=h dial_job.seconds %></td> | |
| <td><%=h dial_job.lines %></td> | |
| <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %>… | |
| diff --git a/web/app/views/dial_results/show.html.erb b/web/app/views/dial_resu… | |
| @@ -4,6 +4,11 @@ | |
| </p> | |
| <p> | |
| + <b>CallerID:</b> | |
| + <%=h @dial_result.cid %> | |
| +</p> | |
| + | |
| +<p> | |
| <b>Dial job:</b> | |
| <%=h @dial_result.dial_job_id %> | |
| </p> | |
| diff --git a/web/app/views/dial_results/view.html.erb b/web/app/views/dial_resu… | |
| @@ -14,6 +14,7 @@ | |
| <table class='table_scaffold' width='100%'> | |
| <tr> | |
| <th>Number</th> | |
| + <th>CallerID</th> | |
| <th>Provider</th> | |
| <th>Completed</th> | |
| <th>Busy</th> | |
| @@ -25,6 +26,7 @@ | |
| <% for dial_result in @dial_results.sort{|a,b| a.number <=> b.number } %> | |
| <tr> | |
| <td><%=h dial_result.number %></td> | |
| + <td><%=h dial_result.cid %></td> | |
| <td><%=h dial_result.provider.name %></td> | |
| <td><%=h dial_result.completed %></td> | |
| <td><%=h dial_result.busy %></td> | |
| diff --git a/web/app/views/home/about.html.erb b/web/app/views/home/about.html.… | |
| @@ -1 +0,0 @@ | |
| -WarVOX is a project created by H D Moore for Metasploit LLC | |
| diff --git a/web/app/views/home/index.html.erb b/web/app/views/home/index.html.… | |
| @@ -4,9 +4,14 @@ | |
| <h1 class='title'>Introduction</h1> | |
| <b>WarVOX</b> is a suite of tools for exploring, classifying, and auditing the | |
| -telephone system. Unlike normal wardialing tools, WarVOX can be used to assess | |
| -voice lines as well as data. To get started, configure one or more <b>Provider… | |
| -and submit a new <b>Job</b>. | |
| +telephone system. Unlike normal wardialing tools, WarVOX works with the actual | |
| +audio from each call and does not use a modem directly. This model allows | |
| +WarVOX to find and classify a wide range of devices, including modems, through | |
| +direct data analysis. To get started, configure an IAX-capable VoIP provider | |
| +in the <b>Providers</b> section, then submit a new <b>Job</b>. Keep in mind | |
| +that the laws regulating automated dialing can vary by location, it is your | |
| +responsibility to ensure that your local laws and the laws governing the | |
| +target telephone range are respected. | |
| </td><td valign='top' align='center'> | |
| diff --git a/web/app/views/layouts/warvox.html.erb b/web/app/views/layouts/warv… | |
| @@ -5,16 +5,16 @@ | |
| <title><%= @title || "WarVOX" %></title> | |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
| <meta http-equiv="Content-Language" content="en-US" /> | |
| - <%= stylesheet_link_tag 'global' %> | |
| + <%= stylesheet_link_tag 'global', 'lightbox' %> | |
| <!--[if IE 7]><%= stylesheet_link_tag 'ie7' %><![endif]--> | |
| - <%= javascript_include_tag 'custom', 'prototype', 'effects', 'FusionCharts… | |
| + <%= javascript_include_tag 'custom', 'prototype', 'effects', 'FusionCharts… | |
| </head> | |
| <body> | |
| <div id="header"> | |
| <%= render :partial => 'shared/header' %> | |
| -</div> | |
| +</div> | |
| <div id="content"> | |
| <div class="box_full"> | |
| diff --git a/web/app/views/providers/edit.html.erb b/web/app/views/providers/ed… | |
| @@ -2,7 +2,10 @@ | |
| <% form_for(@provider) do |f| %> | |
| <%= f.error_messages %> | |
| - | |
| + <p> | |
| + <%= f.label :enabled %><br /> | |
| + <%= f.check_box :enabled %> | |
| + </p> | |
| <p> | |
| <%= f.label :name %><br /> | |
| <%= f.text_field :name %> | |
| diff --git a/web/app/views/providers/index.html.erb b/web/app/views/providers/i… | |
| @@ -2,6 +2,7 @@ | |
| <h1 class='title'>Providers</h1> | |
| <table class='table_scaffold' width='100%'> | |
| <tr> | |
| + <th>Enabled</th> | |
| <th>Name</th> | |
| <th>Host</th> | |
| <th>Port</th> | |
| @@ -12,6 +13,7 @@ | |
| <% for provider in @providers %> | |
| <tr> | |
| + <td><%=h provider.enabled %></td> | |
| <td><%=h provider.name %></td> | |
| <td><%=h provider.host %></td> | |
| <td><%=h provider.port %></td> | |
| @@ -37,7 +39,6 @@ | |
| <% form_for(@new_provider) do |f| %> | |
| <%= f.error_messages %> | |
| - | |
| <p> | |
| <%= f.label :name, 'The nickname for this provider' %><br /> | |
| <%= f.text_field :name %> | |
| diff --git a/web/app/views/providers/new.html.erb b/web/app/views/providers/new… | |
| @@ -2,7 +2,6 @@ | |
| <% form_for(@provider) do |f| %> | |
| <%= f.error_messages %> | |
| - | |
| <p> | |
| <%= f.label :name, 'The nickname for this provider' %><br /> | |
| <%= f.text_field :name %> | |
| diff --git a/web/app/views/providers/show.html.erb b/web/app/views/providers/sh… | |
| @@ -1,5 +1,9 @@ | |
| <h1 class='title'>View Provider</h1> | |
| <p> | |
| + <b>Enabled:</b> | |
| + <%=h @provider.enabled %> | |
| +</p> | |
| +<p> | |
| <b>Name:</b> | |
| <%=h @provider.name %> | |
| </p> | |
| diff --git a/web/config/environment.rb b/web/config/environment.rb | |
| @@ -58,9 +58,12 @@ Rails::Initializer.run do |config| | |
| # If you change this key, all old sessions will become invalid! | |
| # Make sure the secret is at least 30 characters and all random, | |
| # no regular words or you'll be exposed to dictionary attacks. | |
| + # | |
| + # WarVOX generates a new key for each installation | |
| + # This file is stored under web/config/session.key | |
| config.action_controller.session = { | |
| :session_key => '_warvox', | |
| - :secret => 'feada588709ac0fd51987c264ad6fba018be0e3a579547b9c9978098a… | |
| + :secret => WarVOX::Config.load_session_key | |
| } | |
| # Use the database for sessions instead of the cookie-based default, | |
| diff --git a/web/db/migrate/20090303204859_add_cid_mask_to_dial_jobs.rb b/web/d… | |
| @@ -0,0 +1,9 @@ | |
| +class AddCidMaskToDialJobs < ActiveRecord::Migration | |
| + def self.up | |
| + add_column :dial_jobs, :cid_mask, :string | |
| + end | |
| + | |
| + def self.down | |
| + remove_column :dial_jobs, :cid_mask | |
| + end | |
| +end | |
| diff --git a/web/db/migrate/20090303204917_add_cid_to_dial_results.rb b/web/db/… | |
| @@ -0,0 +1,9 @@ | |
| +class AddCidToDialResults < ActiveRecord::Migration | |
| + def self.up | |
| + add_column :dial_results, :cid, :string | |
| + end | |
| + | |
| + def self.down | |
| + remove_column :dial_results, :cid | |
| + end | |
| +end | |
| diff --git a/web/db/migrate/20090303225838_add_enabled_to_providers.rb b/web/db… | |
| @@ -0,0 +1,9 @@ | |
| +class AddEnabledToProviders < ActiveRecord::Migration | |
| + def self.up | |
| + add_column :providers, :enabled, :boolean | |
| + end | |
| + | |
| + def self.down | |
| + remove_column :providers, :enabled | |
| + end | |
| +end | |
| diff --git a/web/db/schema.rb b/web/db/schema.rb | |
| @@ -9,7 +9,7 @@ | |
| # | |
| # It's strongly recommended to check this file into your version control syste… | |
| -ActiveRecord::Schema.define(:version => 20090301084459) do | |
| +ActiveRecord::Schema.define(:version => 20090303225838) do | |
| create_table "dial_jobs", :force => true do |t| | |
| t.string "range" | |
| @@ -22,6 +22,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do | |
| t.boolean "processed" | |
| t.datetime "created_at" | |
| t.datetime "updated_at" | |
| + t.string "cid_mask" | |
| end | |
| create_table "dial_results", :force => true do |t| | |
| @@ -37,6 +38,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do | |
| t.datetime "created_at" | |
| t.datetime "updated_at" | |
| t.datetime "processed_at" | |
| + t.string "cid" | |
| end | |
| create_table "providers", :force => true do |t| | |
| @@ -48,6 +50,7 @@ ActiveRecord::Schema.define(:version => 20090301084459) do | |
| t.integer "lines" | |
| t.datetime "created_at" | |
| t.datetime "updated_at" | |
| + t.boolean "enabled" | |
| end | |
| end | |
| diff --git a/web/public/images/close.gif b/web/public/images/close.gif | |
| Binary files differ. | |
| diff --git a/web/public/images/loading.gif b/web/public/images/loading.gif | |
| Binary files differ. | |
| diff --git a/web/public/images/overlay.png b/web/public/images/overlay.png | |
| Binary files differ. | |
| diff --git a/web/public/javascripts/lightbox.js b/web/public/javascripts/lightb… | |
| @@ -0,0 +1,426 @@ | |
| +/* | |
| + Lightbox JS: Fullsize Image Overlays | |
| + by Lokesh Dhakar - http://www.huddletogether.com | |
| + | |
| + For more information on this script, visit: | |
| + http://huddletogether.com/projects/lightbox/ | |
| + | |
| + Script featured on Dynamic Drive code library Jan 24th, 06': | |
| + http://www.dynamicdrive.com | |
| + | |
| + Licensed under the Creative Commons Attribution 2.5 License - http://c… | |
| + (basically, do anything you want, just leave my name and link) | |
| + | |
| + Table of Contents | |
| + ----------------- | |
| + Configuration | |
| + | |
| + Functions | |
| + - getPageScroll() | |
| + - getPageSize() | |
| + - pause() | |
| + - getKey() | |
| + - listenKey() | |
| + - showLightbox() | |
| + - hideLightbox() | |
| + - initLightbox() | |
| + - addLoadEvent() | |
| + | |
| + Function Calls | |
| + - addLoadEvent(initLightbox) | |
| + | |
| +*/ | |
| + | |
| + | |
| + | |
| +// | |
| +// Configuration | |
| +// | |
| + | |
| +// If you would like to use a custom loading image or close button reference t… | |
| +var loadingImage = '/images/loading.gif'; | |
| +var closeButton = '/images/close.gif'; | |
| + | |
| + | |
| + | |
| + | |
| + | |
| +// | |
| +// getPageScroll() | |
| +// Returns array with x,y page scroll values. | |
| +// Core code from - quirksmode.org | |
| +// | |
| +function getPageScroll(){ | |
| + | |
| + var yScroll; | |
| + | |
| + if (self.pageYOffset) { | |
| + yScroll = self.pageYOffset; | |
| + } else if (document.documentElement && document.documentElement.scroll… | |
| + yScroll = document.documentElement.scrollTop; | |
| + } else if (document.body) {// all other Explorers | |
| + yScroll = document.body.scrollTop; | |
| + } | |
| + | |
| + arrayPageScroll = new Array('',yScroll) | |
| + return arrayPageScroll; | |
| +} | |
| + | |
| + | |
| + | |
| +// | |
| +// getPageSize() | |
| +// Returns array with page width, height and window width, height | |
| +// Core code from - quirksmode.org | |
| +// Edit for Firefox by pHaez | |
| +// | |
| +function getPageSize(){ | |
| + | |
| + var xScroll, yScroll; | |
| + | |
| + if (window.innerHeight && window.scrollMaxY) { | |
| + xScroll = document.body.scrollWidth; | |
| + yScroll = window.innerHeight + window.scrollMaxY; | |
| + } else if (document.body.scrollHeight > document.body.offsetHeight){ /… | |
| + xScroll = document.body.scrollWidth; | |
| + yScroll = document.body.scrollHeight; | |
| + } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozil… | |
| + xScroll = document.body.offsetWidth; | |
| + yScroll = document.body.offsetHeight; | |
| + } | |
| + | |
| + var windowWidth, windowHeight; | |
| + if (self.innerHeight) { // all except Explorer | |
| + windowWidth = self.innerWidth; | |
| + windowHeight = self.innerHeight; | |
| + } else if (document.documentElement && document.documentElement.client… | |
| + windowWidth = document.documentElement.clientWidth; | |
| + windowHeight = document.documentElement.clientHeight; | |
| + } else if (document.body) { // other Explorers | |
| + windowWidth = document.body.clientWidth; | |
| + windowHeight = document.body.clientHeight; | |
| + } | |
| + | |
| + // for small pages with total height less then height of the viewport | |
| + if(yScroll < windowHeight){ | |
| + pageHeight = windowHeight; | |
| + } else { | |
| + pageHeight = yScroll; | |
| + } | |
| + | |
| + // for small pages with total width less then width of the viewport | |
| + if(xScroll < windowWidth){ | |
| + pageWidth = windowWidth; | |
| + } else { | |
| + pageWidth = xScroll; | |
| + } | |
| + | |
| + | |
| + arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeigh… | |
| + return arrayPageSize; | |
| +} | |
| + | |
| + | |
| +// | |
| +// pause(numberMillis) | |
| +// Pauses code execution for specified time. Uses busy code, not good. | |
| +// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602 | |
| +// | |
| +function pause(numberMillis) { | |
| + var now = new Date(); | |
| + var exitTime = now.getTime() + numberMillis; | |
| + while (true) { | |
| + now = new Date(); | |
| + if (now.getTime() > exitTime) | |
| + return; | |
| + } | |
| +} | |
| + | |
| +// | |
| +// getKey(key) | |
| +// Gets keycode. If 'x' is pressed then it hides the lightbox. | |
| +// | |
| + | |
| +function getKey(e){ | |
| + if (e == null) { // ie | |
| + keycode = event.keyCode; | |
| + } else { // mozilla | |
| + keycode = e.which; | |
| + } | |
| + key = String.fromCharCode(keycode).toLowerCase(); | |
| + | |
| + if(key == 'x'){ hideLightbox(); } | |
| +} | |
| + | |
| + | |
| +// | |
| +// listenKey() | |
| +// | |
| +function listenKey () { document.onkeypress = getKey; } | |
| + | |
| + | |
| +// | |
| +// showLightbox() | |
| +// Preloads images. Pleaces new image in lightbox then centers and displays. | |
| +// | |
| +function showLightbox(objLink) | |
| +{ | |
| + // prep objects | |
| + var objOverlay = document.getElementById('overlay'); | |
| + var objLightbox = document.getElementById('lightbox'); | |
| + var objCaption = document.getElementById('lightboxCaption'); | |
| + var objImage = document.getElementById('lightboxImage'); | |
| + var objLoadingImage = document.getElementById('loadingImage'); | |
| + var objLightboxDetails = document.getElementById('lightboxDetails'); | |
| + | |
| + | |
| + var arrayPageSize = getPageSize(); | |
| + var arrayPageScroll = getPageScroll(); | |
| + | |
| + // center loadingImage if it exists | |
| + if (objLoadingImage) { | |
| + objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageS… | |
| + objLoadingImage.style.left = (((arrayPageSize[0] - 20 - objLoa… | |
| + objLoadingImage.style.display = 'block'; | |
| + } | |
| + | |
| + // set height of Overlay to take up whole page and show | |
| + objOverlay.style.height = (arrayPageSize[1] + 'px'); | |
| + objOverlay.style.display = 'block'; | |
| + | |
| + // preload image | |
| + imgPreload = new Image(); | |
| + | |
| + imgPreload.onload=function(){ | |
| + objImage.src = objLink.href; | |
| + | |
| + // center lightbox and make sure that the top and left values … | |
| + // and the image placed outside the viewport | |
| + var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35… | |
| + var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width)… | |
| + | |
| + objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTo… | |
| + objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightbox… | |
| + | |
| + | |
| + objLightboxDetails.style.width = imgPreload.width + 'px'; | |
| + | |
| + if(objLink.getAttribute('title')){ | |
| + objCaption.style.display = 'block'; | |
| + //objCaption.style.width = imgPreload.width + 'px'; | |
| + objCaption.innerHTML = objLink.getAttribute('title'); | |
| + } else { | |
| + objCaption.style.display = 'none'; | |
| + } | |
| + | |
| + // A small pause between the image loading and displaying is r… | |
| + // this prevents the previous image displaying for a short bur… | |
| + if (navigator.appVersion.indexOf("MSIE")!=-1){ | |
| + pause(250); | |
| + } | |
| + | |
| + if (objLoadingImage) { objLoadingImage.style.display = … | |
| + objLightbox.style.display = 'block'; | |
| + | |
| + // After image is loaded, update the overlay height as the new… | |
| + // increased the overall page height. | |
| + arrayPageSize = getPageSize(); | |
| + objOverlay.style.height = (arrayPageSize[1] + 'px'); | |
| + | |
| + // Check for 'x' keypress | |
| + listenKey(); | |
| + | |
| + return false; | |
| + } | |
| + | |
| + imgPreload.src = objLink.href; | |
| + | |
| +} | |
| + | |
| + | |
| + | |
| + | |
| + | |
| +// | |
| +// hideLightbox() | |
| +// | |
| +function hideLightbox() | |
| +{ | |
| + // get objects | |
| + objOverlay = document.getElementById('overlay'); | |
| + objLightbox = document.getElementById('lightbox'); | |
| + | |
| + // hide lightbox and overlay | |
| + objOverlay.style.display = 'none'; | |
| + objLightbox.style.display = 'none'; | |
| + | |
| + // disable keypress listener | |
| + document.onkeypress = ''; | |
| +} | |
| + | |
| + | |
| + | |
| + | |
| +// | |
| +// initLightbox() | |
| +// Function runs on window load, going through link tags looking for rel="ligh… | |
| +// These links receive onclick events that enable the lightbox display for the… | |
| +// The function also inserts html markup at the top of the page which will be … | |
| +// container for the overlay pattern and the inline image. | |
| +// | |
| +function initLightbox() | |
| +{ | |
| + | |
| + if (!document.getElementsByTagName){ return; } | |
| + var anchors = document.getElementsByTagName("a"); | |
| + | |
| + // loop through all anchor tags | |
| + for (var i=0; i<anchors.length; i++){ | |
| + var anchor = anchors[i]; | |
| + | |
| + if (anchor.getAttribute("href") && (anchor.getAttribute("rel")… | |
| + anchor.onclick = function () {showLightbox(this); retu… | |
| + } | |
| + } | |
| + | |
| + // the rest of this code inserts html at the top of the page that look… | |
| + // | |
| + // <div id="overlay"> | |
| + // <a href="#" onclick="hideLightbox(); return false;">… | |
| + // </div> | |
| + // <div id="lightbox"> | |
| + // <a href="#" onclick="hideLightbox(); return false;" … | |
| + // <img id="closeButton" /> | |
| + // <img id="lightboxImage" /> | |
| + // </a> | |
| + // <div id="lightboxDetails"> | |
| + // <div id="lightboxCaption"></div> | |
| + // <div id="keyboardMsg"></div> | |
| + // </div> | |
| + // </div> | |
| + | |
| + var objBody = document.getElementsByTagName("body").item(0); | |
| + | |
| + // create overlay div and hardcode some functional styles (aesthetic s… | |
| + var objOverlay = document.createElement("div"); | |
| + objOverlay.setAttribute('id','overlay'); | |
| + objOverlay.onclick = function () {hideLightbox(); return false;} | |
| + objOverlay.style.display = 'none'; | |
| + objOverlay.style.position = 'absolute'; | |
| + objOverlay.style.top = '0'; | |
| + objOverlay.style.left = '0'; | |
| + objOverlay.style.zIndex = '90'; | |
| + objOverlay.style.width = '100%'; | |
| + objBody.insertBefore(objOverlay, objBody.firstChild); | |
| + | |
| + var arrayPageSize = getPageSize(); | |
| + var arrayPageScroll = getPageScroll(); | |
| + | |
| + // preload and create loader image | |
| + var imgPreloader = new Image(); | |
| + | |
| + // if loader image found, create link to hide lightbox and create load… | |
| + imgPreloader.onload=function(){ | |
| + | |
| + var objLoadingImageLink = document.createElement("a"); | |
| + objLoadingImageLink.setAttribute('href','#'); | |
| + objLoadingImageLink.onclick = function () {hideLightbox(); ret… | |
| + objOverlay.appendChild(objLoadingImageLink); | |
| + | |
| + var objLoadingImage = document.createElement("img"); | |
| + objLoadingImage.src = loadingImage; | |
| + objLoadingImage.setAttribute('id','loadingImage'); | |
| + objLoadingImage.style.position = 'absolute'; | |
| + objLoadingImage.style.zIndex = '150'; | |
| + objLoadingImageLink.appendChild(objLoadingImage); | |
| + | |
| + imgPreloader.onload=function(){}; // clear onLoa… | |
| + | |
| + return false; | |
| + } | |
| + | |
| + imgPreloader.src = loadingImage; | |
| + | |
| + // create lightbox div, same note about styles as above | |
| + var objLightbox = document.createElement("div"); | |
| + objLightbox.setAttribute('id','lightbox'); | |
| + objLightbox.style.display = 'none'; | |
| + objLightbox.style.position = 'absolute'; | |
| + objLightbox.style.zIndex = '100'; | |
| + objBody.insertBefore(objLightbox, objOverlay.nextSibling); | |
| + | |
| + // create link | |
| + var objLink = document.createElement("a"); | |
| + objLink.setAttribute('href','#'); | |
| + objLink.setAttribute('title','Click to close'); | |
| + objLink.onclick = function () {hideLightbox(); return false;} | |
| + objLightbox.appendChild(objLink); | |
| + | |
| + // preload and create close button image | |
| + var imgPreloadCloseButton = new Image(); | |
| + | |
| + // if close button image found, | |
| + imgPreloadCloseButton.onload=function(){ | |
| + | |
| + var objCloseButton = document.createElement("img"); | |
| + objCloseButton.src = closeButton; | |
| + objCloseButton.setAttribute('id','closeButton'); | |
| + objCloseButton.style.position = 'absolute'; | |
| + objCloseButton.style.zIndex = '200'; | |
| + objLink.appendChild(objCloseButton); | |
| + | |
| + return false; | |
| + } | |
| + | |
| + imgPreloadCloseButton.src = closeButton; | |
| + | |
| + // create image | |
| + var objImage = document.createElement("img"); | |
| + objImage.setAttribute('id','lightboxImage'); | |
| + objLink.appendChild(objImage); | |
| + | |
| + // create details div, a container for the caption and keyboard message | |
| + var objLightboxDetails = document.createElement("div"); | |
| + objLightboxDetails.setAttribute('id','lightboxDetails'); | |
| + objLightbox.appendChild(objLightboxDetails); | |
| + | |
| + // create caption | |
| + var objCaption = document.createElement("div"); | |
| + objCaption.setAttribute('id','lightboxCaption'); | |
| + objCaption.style.display = 'none'; | |
| + objLightboxDetails.appendChild(objCaption); | |
| + | |
| + // create keyboard message | |
| + var objKeyboardMsg = document.createElement("div"); | |
| + objKeyboardMsg.setAttribute('id','keyboardMsg'); | |
| + objKeyboardMsg.innerHTML = 'press <kbd>x</kbd> to close'; | |
| + objLightboxDetails.appendChild(objKeyboardMsg); | |
| + | |
| + | |
| +} | |
| + | |
| + | |
| + | |
| + | |
| +// | |
| +// addLoadEvent() | |
| +// Adds event to window.onload without overwriting currently assigned onload f… | |
| +// Function found at Simon Willison's weblog - http://simon.incutio.com/ | |
| +// | |
| +function addLoadEvent(func) | |
| +{ | |
| + var oldonload = window.onload; | |
| + if (typeof window.onload != 'function'){ | |
| + window.onload = func; | |
| + } else { | |
| + window.onload = function(){ | |
| + oldonload(); | |
| + func(); | |
| + } | |
| + } | |
| + | |
| +} | |
| + | |
| + | |
| + | |
| +addLoadEvent(initLightbox); // run initLightbox onLoad | |
| diff --git a/web/public/stylesheets/lightbox.css b/web/public/stylesheets/light… | |
| @@ -0,0 +1,26 @@ | |
| +#lightbox{ | |
| + background-color:#eee; | |
| + padding: 10px; | |
| + border-bottom: 2px solid #666; | |
| + border-right: 2px solid #666; | |
| + } | |
| +#lightboxDetails{ | |
| + font-size: 0.8em; | |
| + padding-top: 0.4em; | |
| + } | |
| +#lightboxCaption{ float: left; } | |
| +#keyboardMsg{ float: right; } | |
| +#closeButton{ top: 5px; right: 5px; } | |
| + | |
| +#lightbox img{ border: none; clear: both;} | |
| +#overlay img{ border: none; } | |
| + | |
| +#overlay{ background-image: url(overlay.png); } | |
| + | |
| +* html #overlay{ | |
| + background-color: #333; | |
| + back\ground-color: transparent; | |
| + background-image: url(blank.gif); | |
| + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overla… | |
| + } | |
| + | |
| +\ No newline at end of file |