├── .dockerignore ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── Makefile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── datatables │ │ ├── jquery.dataTables.css │ │ └── jquery.datatables.css │ ├── images │ │ ├── .keep │ │ ├── balloon.png │ │ ├── bluefade.jpg │ │ ├── close.gif │ │ ├── favicon.ico │ │ ├── left-round.png │ │ ├── lightbox-blank.gif │ │ ├── lightbox-btn-close.gif │ │ ├── lightbox-btn-next.gif │ │ ├── lightbox-btn-prev.gif │ │ ├── lightbox-ico-loading.gif │ │ ├── loading.gif │ │ ├── logo.png │ │ ├── logo_bigger.png │ │ ├── logo_light.png │ │ ├── logo_raw.xcf │ │ ├── musicplayer.swf │ │ ├── overlay.png │ │ ├── player.swf │ │ ├── rails.png │ │ ├── right-round.png │ │ ├── round_bot.png │ │ ├── round_top.png │ │ ├── search.png │ │ ├── ubaplayer-btn.png │ │ └── ubaplayer-loading.gif │ ├── javascripts │ │ ├── analyze │ │ │ ├── _index.coffee │ │ │ └── view.coffee │ │ ├── application.js │ │ ├── bootstrap-lightbox.js │ │ ├── bootstrap.js.coffee │ │ ├── dataTables.filteringDelay.js │ │ ├── dataTables.fnReloadAjax.js │ │ ├── dataTables.hiddenTitle.js │ │ ├── dataTables_overrides.js │ │ ├── highcharts.js │ │ ├── html5.js │ │ ├── jobs │ │ │ └── view_results.coffee │ │ ├── jquery.table.coffee │ │ ├── rails.js │ │ └── swfobject.js │ └── stylesheets │ │ ├── application.css.scss.erb │ │ ├── bootstrap-lightbox.css │ │ ├── bootstrap_and_overrides.css │ │ ├── bootstrap_and_overrides.css.less │ │ ├── formtastic-overrides.css │ │ └── jquery.lightbox-0.5.css ├── controllers │ ├── analyze_controller.rb │ ├── application_controller.rb │ ├── calls_controller.rb │ ├── concerns │ │ └── .keep │ ├── home_controller.rb │ ├── jobs_controller.rb │ ├── projects_controller.rb │ ├── providers_controller.rb │ ├── settings_controller.rb │ ├── user_sessions_controller.rb │ └── users_controller.rb ├── helpers │ ├── analyze_helper.rb │ ├── application_helper.rb │ ├── calls_helper.rb │ ├── home_helper.rb │ ├── jobs_helper.rb │ ├── projects_helper.rb │ ├── providers_helper.rb │ ├── settings_helper.rb │ ├── user_sessions_helper.rb │ └── users_helper.rb ├── models │ ├── application_record.rb │ ├── call.rb │ ├── call_medium.rb │ ├── concerns │ │ └── .keep │ ├── job.rb │ ├── line.rb │ ├── line_attribute.rb │ ├── project.rb │ ├── provider.rb │ ├── settings.rb │ ├── signature.rb │ ├── signature_fp.rb │ ├── user.rb │ └── user_session.rb └── views │ ├── analyze │ ├── _index.json.erb │ ├── _view.json.erb │ ├── index.html.erb │ ├── show.html.erb │ ├── view.html.erb │ └── view_matches.html.erb │ ├── application │ └── _nav.html.erb │ ├── calls │ ├── analyze.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── home │ ├── about.html.erb │ ├── check.html.erb │ └── index.html.erb │ ├── jobs │ ├── _view_results.json.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── new_analyze.html.erb │ ├── new_dialer.html.erb │ ├── results.html.erb │ ├── run.html.erb │ └── view_results.html.erb │ ├── layouts │ ├── application.html.erb │ └── login.html.erb │ ├── projects │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── providers │ ├── edit.html.erb │ ├── index.html.erb │ └── new.html.erb │ ├── settings │ └── index.html.erb │ ├── shared │ ├── _call_info.html.erb │ ├── _call_signal.html.erb │ ├── _call_type.html.erb │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _lightbox_freq.html.erb │ ├── _lightbox_sig.html.erb │ └── graphs │ │ ├── _call_results.html.erb │ │ ├── _lines_by_type.html.erb │ │ └── _sparkline.html.erb │ ├── user_sessions │ └── new.html.erb │ └── users │ ├── _form.html.erb │ ├── edit.html.erb │ ├── new.html.erb │ └── show.html.erb ├── bin ├── adduser ├── analyze_result.rb ├── audio_compare.rb ├── audio_raw_to_flac.rb ├── audio_raw_to_fprint.rb ├── audio_raw_to_speech.rb ├── audio_raw_to_wav.rb ├── audio_trim.rb ├── bundle ├── cache_clear.rb ├── export_audio.rb ├── export_list.rb ├── iaxrecord.rb ├── identify_matches.rb ├── import_audio.rb ├── rails ├── rake ├── resetpw ├── setup ├── update ├── verify_install.rb ├── warvox ├── worker.rb └── worker_manager.rb ├── config.ru ├── config ├── application.rb ├── blacklist.txt ├── boot.rb ├── cable.yml ├── classifiers │ ├── .keep │ ├── 01.default.rb │ └── 99.default.rb ├── database.yml.example ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── formtastic.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── session_store.rb │ ├── warvox.rb │ └── wrap_parameters.rb ├── locales │ ├── en.bootstrap.yml │ └── en.yml ├── routes.rb ├── secrets.yml.example ├── signatures │ └── .keep ├── unicorn.rb └── warvox.conf ├── db ├── migrate │ ├── .keep │ ├── 20121228171549_initial_schema.rb │ └── 20130113004653_create_reportable_cache.rb ├── schema.rb └── seeds.rb ├── docs ├── BUGS ├── ChangeLog ├── LICENSE └── LICENSE.musicplayer ├── lib ├── assets │ └── .keep ├── tasks │ └── .keep ├── warvox.rb └── warvox │ ├── audio.rb │ ├── audio │ ├── error.rb │ └── raw.rb │ ├── config.rb │ ├── jobs.rb │ ├── jobs │ ├── analysis.rb │ ├── base.rb │ └── dialer.rb │ ├── phone.rb │ └── proto │ ├── iax2.rb │ └── iax2 │ ├── call.rb │ ├── client.rb │ ├── codecs.rb │ ├── codecs │ ├── alaw.rb │ ├── g711.rb │ └── mulaw.rb │ └── constants.rb ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── crossdomain.xml ├── favicon.ico └── robots.txt ├── spec ├── factories │ ├── call_media.rb │ ├── calls.rb │ ├── jobs.rb │ ├── lines.rb │ ├── projects.rb │ ├── providers.rb │ ├── settings.rb │ ├── signature_fps.rb │ ├── signatures.rb │ └── users.rb ├── features │ ├── projects_spec.rb │ └── visitor │ │ └── logins_spec.rb ├── models │ ├── call_medium_spec.rb │ ├── call_spec.rb │ ├── job_spec.rb │ ├── line_spec.rb │ ├── project_spec.rb │ ├── provider_spec.rb │ ├── settings_spec.rb │ ├── signature_fp_spec.rb │ ├── signature_spec.rb │ └── user_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── support │ ├── auth_logic_helpers.rb │ └── factory_girl.rb └── tmp └── .keep /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .env 3 | .dockerignore 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | capybara-*.html 3 | .rspec 4 | /log 5 | /tmp 6 | /db/*.sqlite3 7 | /db/*.sqlite3-journal 8 | /public/system 9 | /coverage/ 10 | /spec/tmp 11 | **.orig 12 | rerun.txt 13 | pickle-email-*.html 14 | config/database.yml 15 | config/session.key 16 | 17 | # TODO Comment out these rules if you are OK with secrets being uploaded to the repo 18 | config/initializers/secret_token.rb 19 | config/secrets.yml 20 | 21 | ## Environment normalisation: 22 | /.bundle 23 | /vendor/bundle 24 | 25 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 26 | .rvmrc 27 | 28 | # vagrant and ansible files 29 | .vagrant 30 | playbook.retry 31 | 32 | TODO.md 33 | /public/assets 34 | .env 35 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2.5 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'puma' 4 | gem 'rails', '~> 5.0', '>= 5.0.0.1' 5 | gem 'pg', '~> 0.18' 6 | 7 | gem 'jquery-rails' 8 | gem 'jquery-datatables-rails', git: 'https://github.com/rweng/jquery-datatables-rails.git' 9 | 10 | gem 'kissfft' 11 | gem 'rex', '~> 2.0.10' 12 | 13 | gem 'bootstrap-sass', '>= 3.2.0' 14 | gem 'sass-rails', '~> 5.0' 15 | gem 'coffee-rails', '~> 4.2' 16 | gem 'uglifier', '>= 1.3.0' 17 | gem 'autoprefixer-rails' 18 | 19 | gem 'authlogic', git: 'https://github.com/binarylogic/authlogic.git' 20 | gem 'rails-settings-cached', '>= 0.4.1' 21 | gem 'breadcrumbs_on_rails' 22 | 23 | gem 'formtastic', '>= 3.0.0' 24 | gem 'formtastic-bootstrap', '>= 3.0.0' 25 | gem 'therubyracer' 26 | 27 | gem 'font-awesome-rails' 28 | 29 | gem 'reportable', git: 'https://github.com/hdm/reportable.git', 30 | require: 'saulabs/reportable' 31 | 32 | gem 'will_paginate', '~> 3.0' 33 | gem 'will_paginate-bootstrap' 34 | gem 'dynamic_form', '>= 1.1.4' 35 | gem 'psych_shield' 36 | gem 'scrypt' 37 | 38 | group :development do 39 | gem 'guard-bundler' 40 | gem 'guard-rails' 41 | gem 'guard-rspec' 42 | gem 'rails_layout' 43 | gem 'guard-livereload', '~> 2.4', require: false 44 | gem 'annotate' 45 | gem 'web-console' 46 | end 47 | group :development, :test do 48 | gem 'factory_girl_rails' 49 | gem 'faker' 50 | gem 'rspec-rails', '~> 3.5' 51 | gem 'byebug', platform: :mri 52 | end 53 | group :test do 54 | gem 'capybara' 55 | gem 'pry' 56 | gem 'database_cleaner' 57 | gem 'launchy' 58 | gem 'selenium-webdriver' 59 | gem 'shoulda-matchers', '2.8' 60 | end 61 | 62 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | test: install 4 | bin/verify_install.rb 5 | 6 | install: bundler 7 | 8 | bundler: 9 | @echo "Checking for RubyGems and the Bundler gem..." 10 | @ruby -rrubygems -e 'require "bundler"; puts "OK"' 11 | 12 | @echo "Validating that 'bundle' is in the path..." 13 | which bundle 14 | 15 | @echo "Installing missing gems as needed.." 16 | bundle install 17 | 18 | 19 | database: 20 | @echo "Checking the database.." 21 | RAILS_ENV=production bundle exec rake db:migrate 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WarVOX 2 | 3 | *Notice*: WarVOX is currently unsupported and unmaintained. YMMV. 4 | 5 | WarVOX is released under a BSD-style license. See docs/LICENSE for more details. 6 | 7 | The latest version of this software is available from http://github.com/rapid7/warvox/ 8 | 9 | Questions and suggestions can be sent to: 10 | research[at]rapid7.com 11 | 12 | - [Installing](#installing) 13 | 14 | ## Installing 15 | 16 | WarVOX requires a Linux operating system, preferably Ubuntu or Debian. 17 | 18 | WarVOX requires PostgreSQL 9.1 or newer with the "contrib" package installed for integer array support. 19 | 20 | To get started, install the OS-level dependencies: 21 | ``` 22 | $ sudo apt-get install gnuplot lame build-essential libssl-dev libcurl4-openssl-dev \ 23 | postgresql postgresql-contrib postgresql-common git-core curl libpq-dev sox 24 | ``` 25 | 26 | Install RVM to obtain Ruby 2.2.5 or later 27 | ``` 28 | $ \curl -L https://get.rvm.io | bash -s stable --autolibs=3 --rails 29 | ``` 30 | 31 | After RVM is installed you need to run the rvm script provided 32 | ``` 33 | $ source /usr/local/rvm/scripts/rvm 34 | ``` 35 | 36 | In case you have not installed Ruby 2.2.5 or later by now, do so using RVM. 37 | ``` 38 | $ rvm install ruby-2.2.5 39 | ``` 40 | 41 | Clone this repository to the location you want to install WarVOX: 42 | ``` 43 | $ git clone git://github.com/rapid7/warvox.git /opt/warvox 44 | ``` 45 | 46 | Configure WarVOX: 47 | ``` 48 | $ cd /opt/warvox 49 | $ bundle install 50 | $ make 51 | ``` 52 | 53 | Verify your installation: 54 | ``` 55 | $ bin/verify_install.rb 56 | ``` 57 | 58 | Configure the PostgreSQL account for WarVOX: 59 | ``` 60 | $ sudo su - postgres 61 | $ createuser -s warvox 62 | $ createdb warvox -O warvox 63 | $ psql 64 | psql> alter user warvox with password 'randompass'; 65 | psql> exit 66 | $ exit 67 | ``` 68 | 69 | Copy the example database configuration to database.yml: 70 | ``` 71 | $ cp config/database.yml.example config/database.yml 72 | ``` 73 | 74 | Copy the example secrets configuration to secrets.yml: 75 | ``` 76 | $ cp config/secrets.yml.example config/secrets.yml 77 | ``` 78 | Create a new secrect token: 79 | ``` 80 | $ rake secret > config/session.key 81 | ``` 82 | Modify config/database.yml to include the password set previously 83 | 84 | Initialize the WarVOX database: 85 | ``` 86 | $ make database 87 | ``` 88 | 89 | Add an admin account to WarVOX 90 | ``` 91 | $ bin/adduser admin randompass 92 | ``` 93 | 94 | Start the WarVOX daemons: 95 | ``` 96 | $ bin/warvox 97 | ``` 98 | 99 | or to bind WarVox to all interfaces: 100 | ``` 101 | $ bin/warvox --address 0.0.0.0 102 | ``` 103 | 104 | Access the web interface at http://127.0.0.1:7777/ 105 | 106 | At this point you can configure a new IAX2 provider, create a project, and start making calls. 107 | 108 | ## Assets 109 | 110 | To get assets to show up, you need to first compile assets in production environment: 111 | 112 | ``` 113 | RAILS_ENV=production bundle exec rake assets:precompile 114 | ``` 115 | This will compile all static assets into `public` folder. 116 | 117 | Next, you need to enable the `RAILS_SERVE_STATIC_FILES` environment variable through the terminal: 118 | 119 | ``` 120 | export RAILS_SERVE_STATIC_FILES=true 121 | ``` 122 | or wrap the above in a `.env` file and run source: 123 | 124 | ``` 125 | source .env 126 | ``` 127 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | Web::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/datatables/jquery.dataTables.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/datatables/jquery.dataTables.css -------------------------------------------------------------------------------- /app/assets/datatables/jquery.datatables.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/datatables/jquery.datatables.css -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/balloon.png -------------------------------------------------------------------------------- /app/assets/images/bluefade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/bluefade.jpg -------------------------------------------------------------------------------- /app/assets/images/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/close.gif -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/left-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/left-round.png -------------------------------------------------------------------------------- /app/assets/images/lightbox-blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/lightbox-blank.gif -------------------------------------------------------------------------------- /app/assets/images/lightbox-btn-close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/lightbox-btn-close.gif -------------------------------------------------------------------------------- /app/assets/images/lightbox-btn-next.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/lightbox-btn-next.gif -------------------------------------------------------------------------------- /app/assets/images/lightbox-btn-prev.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/lightbox-btn-prev.gif -------------------------------------------------------------------------------- /app/assets/images/lightbox-ico-loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/lightbox-ico-loading.gif -------------------------------------------------------------------------------- /app/assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/loading.gif -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/logo_bigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/logo_bigger.png -------------------------------------------------------------------------------- /app/assets/images/logo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/logo_light.png -------------------------------------------------------------------------------- /app/assets/images/logo_raw.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/logo_raw.xcf -------------------------------------------------------------------------------- /app/assets/images/musicplayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/musicplayer.swf -------------------------------------------------------------------------------- /app/assets/images/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/overlay.png -------------------------------------------------------------------------------- /app/assets/images/player.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/player.swf -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/images/right-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/right-round.png -------------------------------------------------------------------------------- /app/assets/images/round_bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/round_bot.png -------------------------------------------------------------------------------- /app/assets/images/round_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/round_top.png -------------------------------------------------------------------------------- /app/assets/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/search.png -------------------------------------------------------------------------------- /app/assets/images/ubaplayer-btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/ubaplayer-btn.png -------------------------------------------------------------------------------- /app/assets/images/ubaplayer-loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/images/ubaplayer-loading.gif -------------------------------------------------------------------------------- /app/assets/javascripts/analyze/_index.coffee: -------------------------------------------------------------------------------- 1 | jQuery ($) -> 2 | $ -> 3 | resultsPath = $('#results-path').html() 4 | $resultsTable = $('#results-table') 5 | 6 | # Enable DataTable for the results list. 7 | $resultsDataTable = $resultsTable.table 8 | analysisTab: true 9 | controlBarLocation: $('.analysis-control-bar') 10 | searchInputHint: 'Search Calls' 11 | searchable: true 12 | datatableOptions: 13 | "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>", 14 | "sPaginationType": "bootstrap", 15 | "oLanguage": 16 | "sEmptyTable": "No analysis results." 17 | "sAjaxSource": resultsPath 18 | "aaSorting": [[1, 'asc']] 19 | "aoColumns": [ 20 | {"mDataProp": "checkbox", "bSortable": false, "sWidth": "22px"} 21 | {"mDataProp": "number"} 22 | {"mDataProp": "line_type"} 23 | {"mDataProp": "signal"} 24 | ] 25 | "fnServerData": ( sSource, aoData, fnCallback ) -> 26 | $.getJSON sSource, aoData, (json) -> 27 | fnCallback(json) 28 | $(".xtooltip").tooltip('fixTitle') 29 | $(".xpopover").popover 30 | html: true 31 | placement: 'right' 32 | trigger: 'hover' 33 | delay: { show: 300, hide: 300 } 34 | animation: false 35 | 36 | # Gray out the table during loads. 37 | $("#results-table_processing").watch 'visibility', -> 38 | if $(this).css('visibility') == 'visible' 39 | $resultsTable.css opacity: 0.6 40 | else 41 | $resultsTable.css opacity: 1 42 | 43 | # Display the search bar when the search icon is clicked 44 | $('.button .search').click (e) -> 45 | $filter = $('.dataTables_filter') 46 | $input = $('.dataTables_filter input') 47 | if $filter.css('bottom').charAt(0) == '-' # if (css matches -42px) 48 | # input box is visible, hide it 49 | # only allow user to hide if there is no search string 50 | if !$input.val() || $input.val().length < 1 51 | $filter.css('bottom', '99999999px') 52 | else # input box is invisible, display it 53 | $filter.css('bottom', '-42px') 54 | $input.focus() # auto-focus input 55 | e.preventDefault() 56 | 57 | searchVal = $('.dataTables_filter input').val() 58 | $('.button .search').click() if searchVal && searchVal.length > 0 59 | -------------------------------------------------------------------------------- /app/assets/javascripts/analyze/view.coffee: -------------------------------------------------------------------------------- 1 | jQuery ($) -> 2 | $ -> 3 | resultsPath = $('#results-path').html() 4 | $resultsTable = $('#results-table') 5 | 6 | # Enable DataTable for the results list. 7 | $resultsDataTable = $resultsTable.table 8 | analysisTab: true 9 | controlBarLocation: $('.analysis-control-bar') 10 | searchInputHint: 'Search Calls' 11 | searchable: true 12 | datatableOptions: 13 | "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>", 14 | "sPaginationType": "bootstrap", 15 | "oLanguage": 16 | "sEmptyTable": "No analysis results." 17 | "sAjaxSource": resultsPath 18 | "aaSorting": [[1, 'asc']] 19 | "aoColumns": [ 20 | {"mDataProp": "checkbox", "bSortable": false, "sWidth": "22px"} 21 | {"mDataProp": "number"} 22 | {"mDataProp": "line_type"} 23 | {"mDataProp": "signal"} 24 | ] 25 | "fnServerData": ( sSource, aoData, fnCallback ) -> 26 | $.getJSON sSource, aoData, (json) -> 27 | fnCallback(json) 28 | $(".xtooltip").tooltip('fixTitle') 29 | $(".xpopover").popover 30 | html: true 31 | placement: 'right' 32 | trigger: 'hover' 33 | delay: { show: 300, hide: 300 } 34 | animation: false 35 | 36 | # Gray out the table during loads. 37 | $("#results-table_processing").watch 'visibility', -> 38 | if $(this).css('visibility') == 'visible' 39 | $resultsTable.css opacity: 0.6 40 | else 41 | $resultsTable.css opacity: 1 42 | 43 | # Display the search bar when the search icon is clicked 44 | $('.button .search').click (e) -> 45 | $filter = $('.dataTables_filter') 46 | $input = $('.dataTables_filter input') 47 | if $filter.css('bottom').charAt(0) == '-' # if (css matches -42px) 48 | # input box is visible, hide it 49 | # only allow user to hide if there is no search string 50 | if !$input.val() || $input.val().length < 1 51 | $filter.css('bottom', '99999999px') 52 | else # input box is invisible, display it 53 | $filter.css('bottom', '-42px') 54 | $input.focus() # auto-focus input 55 | e.preventDefault() 56 | 57 | searchVal = $('.dataTables_filter input').val() 58 | $('.button .search').click() if searchVal && searchVal.length > 0 59 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap.js.coffee: -------------------------------------------------------------------------------- 1 | jQuery -> 2 | $("a[rel~=popover], .has-popover").popover() 3 | $("a[rel~=tooltip], .has-tooltip").tooltip() 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/dataTables.filteringDelay.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.dataTableExt.oApi.fnSetFilteringDelay = function ( oSettings, iDelay ) { 2 | /* 3 | * Inputs: object:oSettings - dataTables settings object - automatically given 4 | * integer:iDelay - delay in milliseconds 5 | * Usage: $('#example').dataTable().fnSetFilteringDelay(250); 6 | * Author: Zygimantas Berziunas (www.zygimantas.com) and Allan Jardine 7 | * License: GPL v2 or BSD 3 point style 8 | * Contact: zygimantas.berziunas /AT\ hotmail.com 9 | */ 10 | var 11 | _that = this, 12 | iDelay = (typeof iDelay == 'undefined') ? 250 : iDelay; 13 | 14 | this.each( function ( i ) { 15 | jQuery.fn.dataTableExt.iApiIndex = i; 16 | var 17 | $this = this, 18 | oTimerId = null, 19 | sPreviousSearch = null, 20 | anControl = jQuery( 'input', _that.fnSettings().aanFeatures.f ); 21 | 22 | anControl.unbind( 'keyup' ).bind( 'keyup', function() { 23 | var $$this = $this; 24 | 25 | if (sPreviousSearch === null || sPreviousSearch != anControl.val()) { 26 | window.clearTimeout(oTimerId); 27 | sPreviousSearch = anControl.val(); 28 | oTimerId = window.setTimeout(function() { 29 | jQuery.fn.dataTableExt.iApiIndex = i; 30 | _that.fnFilter( anControl.val() ); 31 | }, iDelay); 32 | } 33 | }); 34 | 35 | return this; 36 | } ); 37 | return this; 38 | } 39 | 40 | /* Example call 41 | $(document).ready(function() { 42 | $('.dataTable').dataTable().fnSetFilteringDelay(); 43 | } ); */ 44 | 45 | -------------------------------------------------------------------------------- /app/assets/javascripts/dataTables.fnReloadAjax.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, fnCallback, bStandingRedraw ) 2 | { 3 | if ( typeof sNewSource != 'undefined' && sNewSource != null ) { 4 | oSettings.sAjaxSource = sNewSource; 5 | } 6 | 7 | // Server-side processing should just call fnDraw 8 | if ( oSettings.oFeatures.bServerSide ) { 9 | this.fnDraw(); 10 | return; 11 | } 12 | 13 | this.oApi._fnProcessingDisplay( oSettings, true ); 14 | var that = this; 15 | var iStart = oSettings._iDisplayStart; 16 | var aData = []; 17 | 18 | this.oApi._fnServerParams( oSettings, aData ); 19 | 20 | oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aData, function(json) { 21 | /* Clear the old information from the table */ 22 | that.oApi._fnClearTable( oSettings ); 23 | 24 | /* Got the data - add it to the table */ 25 | var aData = (oSettings.sAjaxDataProp !== "") ? 26 | that.oApi._fnGetObjectDataFn( oSettings.sAjaxDataProp )( json ) : json; 27 | 28 | for ( var i=0 ; i y) ? 1 : 0)); 7 | }; 8 | 9 | jQuery.fn.dataTableExt.oSort['title-numeric-desc'] = function(a,b) { 10 | var x = a.match(/title="*(-?[0-9]+)/)[1]; 11 | var y = b.match(/title="*(-?[0-9]+)/)[1]; 12 | x = parseFloat( x ); 13 | y = parseFloat( y ); 14 | return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 15 | }; 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/html5.js: -------------------------------------------------------------------------------- 1 | /*! HTML5 Shiv vpre3.6 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 2 | Uncompressed source: https://github.com/aFarkas/html5shiv */ 3 | (function(a,b){function h(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function i(){var a=l.elements;return typeof a=="string"?a.split(" "):a}function j(a){var b={},c=a.createElement,f=a.createDocumentFragment,g=f();a.createElement=function(a){if(!l.shivMethods)return c(a);var f;return b[a]?f=b[a].cloneNode():e.test(a)?f=(b[a]=c(a)).cloneNode():f=c(a),f.canHaveChildren&&!d.test(a)?g.appendChild(f):f},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+i().join().replace(/\w+/g,function(a){return c(a),g.createElement(a),'c("'+a+'")'})+");return n}")(l,g)}function k(a){var b;return a.documentShived?a:(l.shivCSS&&!f&&(b=!!h(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),g||(b=!j(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i,f,g;(function(){var c=b.createElement("a");c.innerHTML="",f="hidden"in c,f&&typeof injectElementWithStyles=="function"&&injectElementWithStyles("#modernizr{}",function(b){b.hidden=!0,f=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle).display=="none"}),g=c.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var l={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:k};a.html5=l,k(b)})(this,document) -------------------------------------------------------------------------------- /app/assets/javascripts/jobs/view_results.coffee: -------------------------------------------------------------------------------- 1 | jQuery ($) -> 2 | $ -> 3 | resultsPath = $('#results-path').html() 4 | $resultsTable = $('#results-table') 5 | 6 | # Enable DataTable for the results list. 7 | $resultsDataTable = $resultsTable.table 8 | analysisTab: true 9 | controlBarLocation: $('.analysis-control-bar') 10 | searchInputHint: 'Search Calls' 11 | searchable: true 12 | datatableOptions: 13 | "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>", 14 | "sPaginationType": "bootstrap", 15 | "oLanguage": 16 | "sEmptyTable": "No results for this job." 17 | "sAjaxSource": resultsPath 18 | "aaSorting": [[1, 'asc']] 19 | "aoColumns": [ 20 | {"mDataProp": "checkbox", "bSortable": false} 21 | {"mDataProp": "number"} 22 | {"mDataProp": "caller_id"} 23 | {"mDataProp": "provider"} 24 | {"mDataProp": "answered"} 25 | {"mDataProp": "busy"} 26 | {"mDataProp": "audio_length"} 27 | {"mDataProp": "ring_length"} 28 | ] 29 | "fnServerData": ( sSource, aoData, fnCallback ) -> 30 | $.getJSON sSource, aoData, (json) -> 31 | fnCallback(json) 32 | $(".xtooltip").tooltip('fixTitle') 33 | $(".xpopover").popover 34 | html: true 35 | placement: 'right' 36 | trigger: 'hover' 37 | delay: { show: 300, hide: 300 } 38 | animation: false 39 | 40 | # Gray out the table during loads. 41 | $("#results-table_processing").watch 'visibility', -> 42 | if $(this).css('visibility') == 'visible' 43 | $resultsTable.css opacity: 0.6 44 | else 45 | $resultsTable.css opacity: 1 46 | 47 | # Display the search bar when the search icon is clicked 48 | $('.button .search').click (e) -> 49 | $filter = $('.dataTables_filter') 50 | $input = $('.dataTables_filter input') 51 | if $filter.css('bottom').charAt(0) == '-' # if (css matches -42px) 52 | # input box is visible, hide it 53 | # only allow user to hide if there is no search string 54 | if !$input.val() || $input.val().length < 1 55 | $filter.css('bottom', '99999999px') 56 | else # input box is invisible, display it 57 | $filter.css('bottom', '-42px') 58 | $input.focus() # auto-focus input 59 | e.preventDefault() 60 | 61 | searchVal = $('.dataTables_filter input').val() 62 | $('.button .search').click() if searchVal && searchVal.length > 0 63 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap-lightbox.css: -------------------------------------------------------------------------------- 1 | /*!========================================================= 2 | * bootstrap-lightbox v0.4.1 - 11/20/2012 3 | * http://jbutz.github.com/bootstrap-lightbox/ 4 | * HEAVILY based off bootstrap-modal.js 5 | * ========================================================== 6 | * Copyright (c) 2012 Jason Butz (http://jasonbutz.info) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ========================================================= */ 20 | .lightbox { 21 | background-color: transparent; 22 | text-align: center; 23 | line-height: 0; 24 | z-index: 1050; 25 | position: relative; 26 | top: 70px; 27 | outline: none; 28 | } 29 | .lightbox .hide { 30 | display: none; 31 | } 32 | .lightbox .in { 33 | display: block; 34 | } 35 | .lightbox-content { 36 | display: inline-block; 37 | padding: 10px; 38 | background-color: #ffffff; 39 | border: 1px solid #999; 40 | border: 1px solid rgba(0, 0, 0, 0.3); 41 | *border: 1px solid #999; 42 | /* IE6-7 */ 43 | 44 | -webkit-border-radius: 6px; 45 | -moz-border-radius: 6px; 46 | border-radius: 6px; 47 | -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 48 | -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 49 | box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 50 | -webkit-background-clip: padding-box; 51 | -moz-background-clip: padding-box; 52 | background-clip: padding-box; 53 | } 54 | .lightbox-header .close { 55 | color: white; 56 | margin-right: -16px; 57 | margin-top: -16px; 58 | font-size: 2em; 59 | opacity: .8; 60 | filter: alpha(opacity=80); 61 | } 62 | .lightbox-header .close :hover { 63 | opacity: .4; 64 | filter: alpha(opacity=40); 65 | } 66 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap_and_overrides.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/assets/stylesheets/bootstrap_and_overrides.css -------------------------------------------------------------------------------- /app/assets/stylesheets/formtastic-overrides.css: -------------------------------------------------------------------------------- 1 | .help-block { 2 | font-size: 13px; 3 | font-style: italic; 4 | } 5 | 6 | .control-label { 7 | font-weight: bold; 8 | font-size: 15px; 9 | } 10 | 11 | .formtastic .stringish input 12 | { 13 | width: auto; 14 | } 15 | 16 | .formtastic .boolean label { 17 | padding: 4px; 18 | font-weight: bold; 19 | font-size: 14px; 20 | } 21 | 22 | .formtastic .boolean .control-label { 23 | display: none; 24 | } 25 | 26 | .formtastic .boolean input { 27 | -moz-appearance: none; 28 | -webkit-appearance: none; 29 | appearance: none; 30 | height: 20px; 31 | width: 20px; 32 | margin-right: 20px; 33 | margin-left: 0; 34 | } 35 | 36 | .formtastic .numeric input { 37 | width: 5em; 38 | } 39 | 40 | .formtastic .stringish input#project_name 41 | { 42 | width: 500px; 43 | } 44 | 45 | .formtastic .text textarea.project_description { 46 | width: 500px; 47 | } 48 | 49 | .formtastic .text textarea.project_includes { 50 | width: 200px; 51 | } 52 | 53 | .formtastic input { 54 | padding: 4px; 55 | } 56 | 57 | .formtastic textarea { 58 | padding: 4px; 59 | } 60 | 61 | .formtastic input.btn { 62 | padding-left: 10px; 63 | padding-right: 10px; 64 | padding-top: 5px; 65 | padding-bottom: 5px; 66 | } 67 | -------------------------------------------------------------------------------- /app/assets/stylesheets/jquery.lightbox-0.5.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery lightBox plugin 3 | * This jQuery plugin was inspired and based on Lightbox 2 by Lokesh Dhakar (http://www.huddletogether.com/projects/lightbox2/) 4 | * and adapted to me for use like a plugin from jQuery. 5 | * @name jquery-lightbox-0.5.css 6 | * @author Leandro Vieira Pinho - http://leandrovieira.com 7 | * @version 0.5 8 | * @date April 11, 2008 9 | * @category jQuery plugin 10 | * @copyright (c) 2008 Leandro Vieira Pinho (leandrovieira.com) 11 | * @license CCAttribution-ShareAlike 2.5 Brazil - http://creativecommons.org/licenses/by-sa/2.5/br/deed.en_US 12 | * @example Visit http://leandrovieira.com/projects/jquery/lightbox/ for more informations about this jQuery plugin 13 | */ 14 | #jquery-overlay { 15 | position: absolute; 16 | top: 0; 17 | left: 0; 18 | z-index: 90; 19 | width: 100%; 20 | height: 500px; 21 | } 22 | #jquery-lightbox { 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | width: 100%; 27 | z-index: 100; 28 | text-align: center; 29 | line-height: 0; 30 | } 31 | #jquery-lightbox a img { border: none; } 32 | #lightbox-container-image-box { 33 | position: relative; 34 | background-color: #fff; 35 | width: 250px; 36 | height: 250px; 37 | margin: 0 auto; 38 | } 39 | #lightbox-container-image { padding: 10px; } 40 | #lightbox-loading { 41 | position: absolute; 42 | top: 40%; 43 | left: 0%; 44 | height: 25%; 45 | width: 100%; 46 | text-align: center; 47 | line-height: 0; 48 | } 49 | #lightbox-nav { 50 | position: absolute; 51 | top: 0; 52 | left: 0; 53 | height: 100%; 54 | width: 100%; 55 | z-index: 10; 56 | } 57 | #lightbox-container-image-box > #lightbox-nav { left: 0; } 58 | #lightbox-nav a { outline: none;} 59 | #lightbox-nav-btnPrev, #lightbox-nav-btnNext { 60 | width: 49%; 61 | height: 100%; 62 | zoom: 1; 63 | display: block; 64 | } 65 | #lightbox-nav-btnPrev { 66 | left: 0; 67 | float: left; 68 | } 69 | #lightbox-nav-btnNext { 70 | right: 0; 71 | float: right; 72 | } 73 | #lightbox-container-image-data-box { 74 | font: 10px Verdana, Helvetica, sans-serif; 75 | background-color: #fff; 76 | margin: 0 auto; 77 | line-height: 1.4em; 78 | overflow: auto; 79 | width: 100%; 80 | padding: 0 10px 0; 81 | } 82 | #lightbox-container-image-data { 83 | padding: 0 10px; 84 | color: #666; 85 | } 86 | #lightbox-container-image-data #lightbox-image-details { 87 | width: 70%; 88 | float: left; 89 | text-align: left; 90 | } 91 | #lightbox-image-details-caption { font-weight: bold; } 92 | #lightbox-image-details-currentNumber { 93 | display: block; 94 | clear: left; 95 | padding-bottom: 1.0em; 96 | } 97 | #lightbox-secNav-btnClose { 98 | width: 66px; 99 | float: right; 100 | padding-bottom: 0.7em; 101 | } -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | helper :all 4 | 5 | helper_method :current_user_session, :current_user 6 | before_action :require_user, :load_project 7 | add_breadcrumb :projects, :root_path 8 | 9 | include ActionView::Helpers::NumberHelper 10 | 11 | private 12 | 13 | def current_user_session 14 | return @current_user_session if defined?(@current_user_session) 15 | @current_user_session = UserSession.find 16 | end 17 | 18 | def current_user 19 | return @current_user if defined?(@current_user) 20 | @current_user = current_user_session && current_user_session.record 21 | end 22 | 23 | def require_user 24 | unless current_user 25 | store_location 26 | flash.now[:notice] = "You must be logged in to access this page" 27 | redirect_to '/login' 28 | return false 29 | end 30 | end 31 | 32 | def require_no_user 33 | if current_user 34 | store_location 35 | flash[:notice] = "You must be logged out to access this page" 36 | redirect_to user_path(current_user) 37 | return false 38 | end 39 | end 40 | 41 | def store_location 42 | session[:return_to] = request.fullpath 43 | end 44 | 45 | def redirect_back_or_default(default) 46 | redirect_to(session[:return_to] || default) 47 | session[:return_to] = nil 48 | end 49 | 50 | def load_project 51 | # Only load this when we are logged in 52 | return true unless current_user 53 | 54 | if params[:project_id] 55 | @project = Project.where(id: params[:project_id].to_i).first 56 | elsif session[:project_id] 57 | @project = Project.where(id: session[:project_id].to_i).first 58 | end 59 | 60 | if @project and @project.id and not (session[:project_id] and session[:project_id] == @project.id) 61 | session[:project_id] = @project.id 62 | end 63 | 64 | true 65 | end 66 | 67 | 68 | end 69 | -------------------------------------------------------------------------------- /app/controllers/calls_controller.rb: -------------------------------------------------------------------------------- 1 | class CallsController < ApplicationController 2 | # GET /calls 3 | # GET /calls.xml 4 | def index 5 | @jobs = @project.jobs.order('id DESC').where('task = ? AND completed_at IS NOT NULL', 'dialer').paginate( 6 | page: params[:page], 7 | per_page: 30 8 | ) 9 | 10 | respond_to do |format| 11 | format.html # index.html.erb 12 | format.xml { render xml: @calls } 13 | end 14 | end 15 | 16 | # GET /calls/1/view 17 | # GET /calls/1/view.xml 18 | def view 19 | @calls = Call.order('id DESC').where(job_id: params[:id]).paginate( 20 | page: params[:page], 21 | per_page: 30 22 | ) 23 | 24 | unless @calls && !@calls.empty? 25 | redirect_to action: :index 26 | return 27 | end 28 | 29 | @call_results = { 30 | Timeout: Call.count(conditions: ['job_id = ? and answered = ?', params[:id], false]), 31 | Busy: Call.count(conditions: ['job_id = ? and busy = ?', params[:id], true]), 32 | Answered: Call.count(conditions: ['job_id = ? and answered = ?', params[:id], true]) 33 | } 34 | 35 | respond_to do |format| 36 | format.html # index.html.erb 37 | format.xml { render xml: @calls } 38 | end 39 | end 40 | 41 | # GET /calls/1 42 | # GET /calls/1.xml 43 | def show 44 | @call = Call.find(params[:id]) 45 | 46 | unless @call 47 | redirect_to action: :index 48 | return 49 | end 50 | 51 | respond_to do |format| 52 | format.html # show.html.erb 53 | format.xml { render xml: @call } 54 | end 55 | end 56 | 57 | # GET /calls/new 58 | # GET /calls/new.xml 59 | def new 60 | @call = Call.new 61 | 62 | respond_to do |format| 63 | format.html # new.html.erb 64 | format.xml { render xml: @call } 65 | end 66 | end 67 | 68 | # GET /calls/1/edit 69 | def edit 70 | @call = Call.find(params[:id]) 71 | end 72 | 73 | # POST /calls 74 | # POST /calls.xml 75 | def create 76 | @call = Call.new(params[:call]) 77 | 78 | respond_to do |format| 79 | if @call.save 80 | flash[:notice] = 'Call was successfully created.' 81 | format.html { redirect_to(@call) } 82 | format.xml { render xml: @call, status: :created, location: @call } 83 | else 84 | format.html { render action: 'new' } 85 | format.xml { render xml: @call.errors, status: :unprocessable_entity } 86 | end 87 | end 88 | end 89 | 90 | # PUT /calls/1 91 | # PUT /calls/1.xml 92 | def update 93 | @call = Call.find(params[:id]) 94 | 95 | respond_to do |format| 96 | if @call.update_attributes(params[:call]) 97 | flash[:notice] = 'Call was successfully updated.' 98 | format.html { redirect_to(@call) } 99 | format.xml { head :ok } 100 | else 101 | format.html { render action: 'edit' } 102 | format.xml { render xml: @call.errors, status: :unprocessable_entity } 103 | end 104 | end 105 | end 106 | 107 | # DELETE /calls/1 108 | # DELETE /calls/1.xml 109 | def destroy 110 | @job = Job.find(params[:id]) 111 | @job.destroy 112 | 113 | respond_to do |format| 114 | format.html { redirect_to action: 'index' } 115 | format.xml { head :ok } 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | 3 | def index 4 | 5 | end 6 | 7 | def about 8 | begin 9 | @has_kissfft = "MISSING" 10 | require 'kissfft' 11 | @has_kissfft = $LOADED_FEATURES.grep(/kissfft/)[0] 12 | rescue ::LoadError 13 | end 14 | end 15 | 16 | def help 17 | end 18 | 19 | def check 20 | @has_project = ( Project.count > 0 ) 21 | @has_provider = ( Provider.where(enabled: true).count > 0 ) 22 | @has_job = ( Job.where(task: 'dialer').count > 0 ) 23 | @has_result = ( Call.where(answered: true ).count > 0 ) 24 | @has_analysis = ( Call.where('analysis_completed_at IS NOT NULL').count > 0 ) 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/providers_controller.rb: -------------------------------------------------------------------------------- 1 | class ProvidersController < ApplicationController 2 | 3 | def index 4 | 5 | @providers = Provider.order('id DESC').paginate( 6 | page: params[:page], 7 | per_page: 10 8 | ) 9 | 10 | @new_provider = Provider.new 11 | @new_provider.enabled = true 12 | 13 | respond_to do |format| 14 | format.html # index.html.erb 15 | format.xml { render xml: @providers } 16 | end 17 | end 18 | 19 | def new 20 | @provider = Provider.new 21 | @provider.enabled = true 22 | @provider.port = 4569 23 | 24 | respond_to do |format| 25 | format.html # new.html.erb 26 | format.xml { render xml: @provider } 27 | end 28 | end 29 | 30 | def edit 31 | @provider = Provider.find(params[:id]) 32 | @provider.pass = "********" 33 | end 34 | 35 | def create 36 | @provider = Provider.new(provider_params) 37 | @provider.enabled = true 38 | 39 | respond_to do |format| 40 | if @provider.save 41 | flash[:notice] = 'Provider was successfully created.' 42 | format.html { redirect_to providers_path } 43 | format.xml { render xml: @provider, status: :created, location: providers_path } 44 | else 45 | format.html { render action: "new" } 46 | format.xml { render xml: @provider.errors, status: :unprocessable_entity } 47 | end 48 | end 49 | end 50 | 51 | 52 | def update 53 | @provider = Provider.find(params[:id]) 54 | 55 | # Dont set the password if its the placeholder 56 | if params[:provider] and params[:provider][:pass] and params[:provider][:pass] == "********" 57 | params[:provider].delete(:pass) 58 | end 59 | 60 | respond_to do |format| 61 | if @provider.update_attributes(provider_params) 62 | flash[:notice] = 'Provider was successfully updated.' 63 | format.html { redirect_to providers_path } 64 | format.xml { head :ok } 65 | else 66 | format.html { render action: "edit" } 67 | format.xml { render xml: @provider.errors, status: :unprocessable_entity } 68 | end 69 | end 70 | end 71 | 72 | def destroy 73 | @provider = Provider.find(params[:id]) 74 | @provider.destroy 75 | 76 | respond_to do |format| 77 | format.html { redirect_to providers_path } 78 | format.xml { head :ok } 79 | end 80 | end 81 | 82 | private 83 | 84 | def provider_params 85 | params.require(:provider).permit(:name, :host, :port, :user, :pass, :lines) 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /app/controllers/settings_controller.rb: -------------------------------------------------------------------------------- 1 | class SettingsController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/user_sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class UserSessionsController < ApplicationController 2 | before_action :require_no_user, only: [:new, :create] 3 | before_action :require_user, only: :destroy 4 | layout 'login' 5 | 6 | def new 7 | @user_session = UserSession.new 8 | end 9 | 10 | def create 11 | @user_session = UserSession.new(user_session_params) 12 | if @user_session.save 13 | redirect_back_or_default projects_path 14 | else 15 | render action: :new 16 | end 17 | end 18 | 19 | def destroy 20 | current_user_session.destroy 21 | redirect_back_or_default login_path 22 | end 23 | 24 | private 25 | 26 | def user_session_params 27 | params.require(:user_session).permit(:login, :password) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :require_no_user, only: [:new, :create] 3 | before_action :require_user, only: [:show, :edit, :update] 4 | 5 | def new 6 | @user = User.new 7 | end 8 | 9 | def create 10 | @user = User.new(user_params) 11 | if @user.save 12 | flash[:notice] = "Account registered!" 13 | redirect_back_or_default user_path(@user) 14 | else 15 | render action: :new 16 | end 17 | end 18 | 19 | def show 20 | @user = @current_user 21 | end 22 | 23 | def edit 24 | @user = @current_user 25 | end 26 | 27 | def update 28 | @user = @current_user # makes our views "cleaner" and more consistent 29 | if @user.update_attributes(user_params) 30 | flash[:notice] = "Account updated!" 31 | redirect_to user_path(@user) 32 | else 33 | render action: :edit 34 | end 35 | end 36 | 37 | private 38 | 39 | def user_params 40 | params.require(:user).permit(:login, :password, :password_confirmation) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/helpers/analyze_helper.rb: -------------------------------------------------------------------------------- 1 | module AnalyzeHelper 2 | 3 | 4 | def fwd_match_html(pct) 5 | %Q| 6 | 7 | #{"%.3f" % pct.to_f}% Match 8 | 9 | 10 | 11 | | 12 | end 13 | 14 | def rev_match_html(pct) 15 | %Q|#{pct}%| 16 | end 17 | 18 | def pct_to_color(pct) 19 | "#" + "20" + (pct.to_i * 2.00).to_i.to_s(16).rjust(2, "0") + "20" 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /app/helpers/calls_helper.rb: -------------------------------------------------------------------------------- 1 | module CallsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/jobs_helper.rb: -------------------------------------------------------------------------------- 1 | module JobsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/projects_helper.rb: -------------------------------------------------------------------------------- 1 | module ProjectsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/providers_helper.rb: -------------------------------------------------------------------------------- 1 | module ProvidersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/settings_helper.rb: -------------------------------------------------------------------------------- 1 | module SettingsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/user_sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module UserSessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/call_medium.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: call_media 4 | # 5 | # id :integer not null, primary key 6 | # call_id :integer not null 7 | # project_id :integer not null 8 | # audio :binary 9 | # mp3 :binary 10 | # png_big :binary 11 | # png_big_dots :binary 12 | # png_big_freq :binary 13 | # png_sig :binary 14 | # png_sig_freq :binary 15 | # 16 | 17 | class CallMedium < ApplicationRecord 18 | belongs_to :call 19 | belongs_to :project 20 | end 21 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/line.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: lines 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # number :text not null 9 | # project_id :integer not null 10 | # line_type :text 11 | # notes :text 12 | # 13 | 14 | class Line < ApplicationRecord 15 | has_many :line_attributes, dependent: :delete_all 16 | belongs_to :project 17 | 18 | def set_attribute(name, value, ctype='text/plain') 19 | la = LineAttribute.where(line_id: self.id, project_id: self.project_id, name: name).first_or_create 20 | la.value = value 21 | la.ctype = ctype 22 | la.save 23 | la 24 | end 25 | 26 | def get_attribute(name) 27 | LineAttribute.where(line_id: self[:id], name: name).first 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/models/line_attribute.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: line_attributes 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # line_id :integer not null 9 | # project_id :integer not null 10 | # name :text not null 11 | # value :binary not null 12 | # content_type :string(255) default("text") 13 | # 14 | 15 | class LineAttribute < ApplicationRecord 16 | belongs_to :line 17 | belongs_to :project 18 | end 19 | -------------------------------------------------------------------------------- /app/models/project.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: projects 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # description :text 10 | # included :text 11 | # excluded :text 12 | # created_by :string(255) 13 | # 14 | 15 | class Project < ApplicationRecord 16 | 17 | validates_presence_of :name 18 | validates_uniqueness_of :name 19 | 20 | # This is optimized for fast project deletion, even with thousands of calls/jobs/lines 21 | has_many :lines, dependent: :delete_all 22 | has_many :line_attributes, dependent: :delete_all 23 | has_many :calls, dependent: :delete_all 24 | has_many :call_media, dependent: :delete_all 25 | has_many :jobs, dependent: :delete_all 26 | end 27 | -------------------------------------------------------------------------------- /app/models/provider.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: providers 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # host :text not null 10 | # port :integer not null 11 | # user :text 12 | # pass :text 13 | # lines :integer default(1), not null 14 | # enabled :boolean default(TRUE) 15 | # 16 | 17 | class Provider < ApplicationRecord 18 | has_many :dial_results 19 | 20 | validates_presence_of :name, :host, :port, :user, :pass, :lines 21 | validates_numericality_of :port, less_than: 65536, greater_than: 0 22 | validates_numericality_of :lines, less_than: 255, greater_than: 0 23 | end 24 | -------------------------------------------------------------------------------- /app/models/settings.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: settings 4 | # 5 | # id :integer not null, primary key 6 | # var :string(255) not null 7 | # value :text 8 | # thing_id :integer 9 | # thing_type :string(30) 10 | # created_at :datetime 11 | # updated_at :datetime 12 | # 13 | 14 | class Settings < RailsSettings::Base 15 | # attr_accessible :var 16 | end 17 | -------------------------------------------------------------------------------- /app/models/signature.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: signatures 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # source :string(255) 10 | # description :text 11 | # category :string(255) 12 | # line_type :string(255) 13 | # risk :integer 14 | # 15 | 16 | class Signature < ApplicationRecord 17 | has_many :signature_fps 18 | 19 | end 20 | -------------------------------------------------------------------------------- /app/models/signature_fp.rb: -------------------------------------------------------------------------------- 1 | class SignatureFp < ApplicationRecord 2 | belongs_to :signature 3 | 4 | end 5 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # login :string(255) not null 7 | # email :string(255) 8 | # crypted_password :string(255) not null 9 | # password_salt :string(255) not null 10 | # persistence_token :string(255) not null 11 | # single_access_token :string(255) not null 12 | # perishable_token :string(255) not null 13 | # login_count :integer default(0), not null 14 | # failed_login_count :integer default(0), not null 15 | # last_request_at :datetime 16 | # current_login_at :datetime 17 | # last_login_at :datetime 18 | # current_login_ip :string(255) 19 | # last_login_ip :string(255) 20 | # created_at :datetime 21 | # updated_at :datetime 22 | # enabled :boolean default(TRUE) 23 | # admin :boolean default(TRUE) 24 | # 25 | 26 | class User < ApplicationRecord 27 | include RailsSettings::Extend 28 | acts_as_authentic do |c| 29 | c.validate_email_field = false 30 | c.merge_validates_length_of_password_field_options minimum: 8 31 | c.merge_validates_length_of_password_confirmation_field_options minimum: 8 32 | c.logged_in_timeout = 1.day 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/models/user_session.rb: -------------------------------------------------------------------------------- 1 | class UserSession < Authlogic::Session::Base 2 | logout_on_timeout true 3 | end 4 | -------------------------------------------------------------------------------- /app/views/analyze/_index.json.erb: -------------------------------------------------------------------------------- 1 | <%- self.formats = ["html"] %> 2 | { 3 | "sEcho": <%= echo_data_tables %>, 4 | "iTotalRecords": <%= @results_total_count.to_json %>, 5 | "iTotalDisplayRecords": <%= @results_total_display_count.to_json %>, 6 | "aaData": [ 7 | <% @results.each_with_index do |result, index| -%> 8 | { 9 | "DT_RowId": <%= dom_id(result).to_json.html_safe%>, 10 | "checkbox": <%= call_checkbox_tag(result) %>, 11 | "number": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_info', :layout => false, :locals => { :call => result }))) %>", 12 | "line_type": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_type', :layout => false, :locals => { :call => result }))) %>", 13 | "signal": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_signal', :layout => false, :locals => { :call => result }))) %>" 14 | }<%= ',' unless index == (@results.size - 1) %> 15 | <% end -%> 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /app/views/analyze/_view.json.erb: -------------------------------------------------------------------------------- 1 | <%- self.formats = ["html"] %> 2 | { 3 | "sEcho": <%= echo_data_tables %>, 4 | "iTotalRecords": <%= @results_total_count.to_json %>, 5 | "iTotalDisplayRecords": <%= @results_total_display_count.to_json %>, 6 | "aaData": [ 7 | <% @results.each_with_index do |result, index| -%> 8 | { 9 | "DT_RowId": <%= dom_id(result).to_json.html_safe%>, 10 | "checkbox": <%= call_checkbox_tag(result) %>, 11 | "number": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_info', :layout => false, :locals => { :call => result }))) %>", 12 | "line_type": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_type', :layout => false, :locals => { :call => result }))) %>", 13 | "signal": "<%= raw(escape_javascript_dq(render(:partial => 'shared/call_signal', :layout => false, :locals => { :call => result }))) %>" 14 | }<%= ',' unless index == (@results.size - 1) %> 15 | <% end -%> 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /app/views/analyze/index.html.erb: -------------------------------------------------------------------------------- 1 | <% include_view_javascript %> 2 | 3 |

Analysis of Project <%= @project.name %>

4 | 5 | 6 | 7 | 10 | 11 |
8 | <%= render :partial => 'shared/graphs/lines_by_type' %> 9 |
12 | 13 | 14 | <%= form_tag do %> 15 | 16 |
17 | 18 | 19 | 22 | 25 |
20 | <%= submit_checkboxes_to(raw(' ReDial'), new_dialer_project_job_path(@project), { :class => "btn any" }) %> 21 | 23 | <%= submit_checkboxes_to(raw(' Delete'), purge_calls_project_job_path, { :class => "btn any ", :confirm => 'Delete selected calls?' }) %> 24 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
<%= check_box_tag "all_results", false %>NumberTypeSignal
46 | 47 | <% end %> 48 | -------------------------------------------------------------------------------- /app/views/analyze/show.html.erb: -------------------------------------------------------------------------------- 1 |

Overview of Job ID <%= @job_id %>

2 | 3 | 4 | 5 | 8 | 9 |
6 | <%= render :partial => 'shared/graphs/lines_by_type' %> 7 |
10 | -------------------------------------------------------------------------------- /app/views/analyze/view.html.erb: -------------------------------------------------------------------------------- 1 | <% include_view_javascript %> 2 | 3 |

Analysis of Scan #<%= @job_id %>

4 | 5 | 6 | 7 | 10 | 11 |
8 | <%= render :partial => 'shared/graphs/lines_by_type' %> 9 |
12 | 13 | 14 | <%= form_tag do %> 15 | 16 |
17 | 18 | 19 | 22 | 25 |
20 | <%= submit_checkboxes_to(raw(' Scan'), new_dialer_job_path, { :class => "btn any" }) %> 21 | 23 | <%= submit_checkboxes_to(raw(' Delete'), purge_calls_job_path, { :class => "btn any ", :confirm => 'Purge selected calls?' }) %> 24 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
<%= check_box_tag "all_results", false %>NumberTypeSignal
46 | 47 | <% end %> 48 | -------------------------------------------------------------------------------- /app/views/analyze/view_matches.html.erb: -------------------------------------------------------------------------------- 1 | <% include_view_javascript %> 2 | 3 | <% call = @result %> 4 | 5 |

6 | <%= @result.number %> 7 | <% if @job_id %> 8 | (Back to Job Analysis) 9 | <% else %> 10 | (Back to Project Analysis) 11 | <% end %> 12 |

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 30 | 33 | 34 | 35 |
NumberTypeSignal
25 | <%= render :partial => 'shared/call_info', :locals => { :call => call } %> 26 | 28 | <%= render :partial => 'shared/call_type', :locals => { :call => call } %> 29 | 31 | <%= render :partial => 'shared/call_signal', :locals => { :call => call } %> 32 |


36 | 37 | <% if @results.length > 0 %> 38 | 39 | 40 | 41 |

<%= pluralize(@results.total_entries, 'Possible Match') %> for <%= @result.number %>

42 | 43 | 44 |
45 | <%= select_match_scope(@match_scopes, params) %> 46 |
47 | 48 | <%= will_paginate @results, :renderer => BootstrapPagination::Rails %> 49 | 50 | 51 | 52 | 53 | 54 | 55 | <% case @match_scope %> 56 | <% when 'project' %> 57 | 58 | <% when 'global' %> 59 | 60 | <% end %> 61 | 62 | 63 | 64 | 65 | 66 | <% @results.each do |call| %> 67 | 68 | 71 | 74 | <% case @match_scope %> 75 | <% when 'project' %> 76 | 77 | <% when 'global' %> 78 | 79 | <% end %> 80 | 83 | 86 | 87 | <% end %> 88 | 89 |
ScoreNumberJobProjectTypeSignal
69 | <%= raw(fwd_match_html(call.matchscore)) %> 70 | 72 | <%= render :partial => 'shared/call_info', :locals => { :call => call } %> 73 | <%= call.job.id %><%= link_to h(call.project.name), project_path(call.project) %> 81 | <%= render :partial => 'shared/call_type', :locals => { :call => call } %> 82 | 84 | <%= render :partial => 'shared/call_signal', :locals => { :call => call } %> 85 |
90 | 91 | <%= will_paginate @results, :renderer => BootstrapPagination::Rails %> 92 | 93 | 94 | <% else %> 95 | 96 |

No Matches for <%= @result.number %> (<%= @result.created_at.strftime("%Y-%m-%d") %>)

97 | 98 | <% end %> 99 | -------------------------------------------------------------------------------- /app/views/application/_nav.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 70 | -------------------------------------------------------------------------------- /app/views/calls/analyze.html.erb: -------------------------------------------------------------------------------- 1 | <% if @dial_data_todo.length > 0 %> 2 | 3 |

4 | Analyzing Audio for <%= @dial_data_total-@dial_data_done %> of <%= @dial_data_total %> Calls... 5 |

6 | 7 | 8 | 9 | <% if @dial_data_done > 0 %> 10 | 13 | <% end %> 14 | 15 |
11 | <%= render :partial => 'shared/graphs/lines_by_type' %> 12 |
16 | 17 | 20 | 21 | <% else %> 22 | 23 |

No Completed Calls Found

24 | 25 | <% end %> 26 | 27 |
28 | -------------------------------------------------------------------------------- /app/views/calls/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing call

2 | 3 | <%= form_for(@call) do |f| %> 4 | <%= f.error_messages %> 5 | 6 |

7 | <%= f.label :number %>
8 | <%= f.text_field :number %> 9 |

10 |

11 | <%= f.label :cid %>
12 | <%= f.text_field :cid %> 13 |

14 |

15 | <%= f.label :job_id %>
16 | <%= f.text_field :job_id %> 17 |

18 |

19 | <%= f.label :provider %>
20 | <%= f.text_field :provider %> 21 |

22 |

23 | <%= f.label :completed %>
24 | <%= f.check_box :completed %> 25 |

26 |

27 | <%= f.label :busy %>
28 | <%= f.check_box :busy %> 29 |

30 |

31 | <%= f.label :seconds %>
32 | <%= f.text_field :seconds %> 33 |

34 |

35 | <%= f.label :ringtime %>
36 | <%= f.text_field :ringtime %> 37 |

38 |

39 | <%= f.label :rawfile %>
40 | <%= f.text_field :rawfile %> 41 |

42 |

43 | <%= f.submit "Update" %> 44 |

45 | <% end %> 46 | 47 | <%= link_to 'Show', @call %> | 48 | <%= link_to 'Back', calls_path(@project) %> 49 | -------------------------------------------------------------------------------- /app/views/calls/index.html.erb: -------------------------------------------------------------------------------- 1 | <% if @jobs.length > 0 %> 2 |

Completed Jobs

3 | 4 | <%= will_paginate @jobs, :renderer => BootstrapPagination::Rails %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @jobs.each do |job| %> 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 43 | 44 | 45 | <% end %> 46 | 47 |
IDRangeCallerIDConnectedDateActions
<%= job.id %><%= job.range %><%= job.cid_mask %><%= ( 24 | job.calls.where("analysis_completed_at IS NOT NULL").count.to_s + 25 | "/" + 26 | job.calls.count.to_s 27 | )%><%= job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %> 31 | 32 | 33 | 34 | <% if job.calls.where("analysis_completed_at IS NOT NULL").count > 0 %> 35 | 36 | 37 | <% else %> 38 | 39 | <% end %> 40 | 41 | 42 |
48 | 49 | <%= will_paginate @jobs, :renderer => BootstrapPagination::Rails %> 50 | 51 | <% else %> 52 | 53 |

No Completed Jobs

54 |
55 | 56 | <% end %> 57 | 58 | Start Job 59 | -------------------------------------------------------------------------------- /app/views/calls/new.html.erb: -------------------------------------------------------------------------------- 1 |

New call

2 | 3 | <%= form_for(@call) do |f| %> 4 | <%= f.error_messages %> 5 | 6 |

7 | <%= f.label :number %>
8 | <%= f.text_field :number %> 9 |

10 |

11 | <%= f.label :job_id %>
12 | <%= f.text_field :job_id %> 13 |

14 |

15 | <%= f.label :provider %>
16 | <%= f.text_field :provider %> 17 |

18 |

19 | <%= f.label :completed %>
20 | <%= f.check_box :completed %> 21 |

22 |

23 | <%= f.label :busy %>
24 | <%= f.check_box :busy %> 25 |

26 |

27 | <%= f.label :seconds %>
28 | <%= f.text_field :seconds %> 29 |

30 |

31 | <%= f.label :ringtime %>
32 | <%= f.text_field :ringtime %> 33 |

34 |

35 | <%= f.label :rawfile %>
36 | <%= f.text_field :rawfile %> 37 |

38 |

39 | <%= f.submit "Create" %> 40 |

41 | <% end %> 42 | 43 | <%= link_to 'Back', calls_path(@project) %> 44 | -------------------------------------------------------------------------------- /app/views/calls/show.html.erb: -------------------------------------------------------------------------------- 1 |

2 | Number: 3 | <%=h @call.number %> 4 |

5 | 6 |

7 | CallerID: 8 | <%=h @call.cid %> 9 |

10 | 11 |

12 | Dial job: 13 | <%=h @call.job_id %> 14 |

15 | 16 |

17 | Provider: 18 | <%=h @call.provider %> 19 |

20 | 21 |

22 | Completed: 23 | <%=h @call.completed %> 24 |

25 | 26 |

27 | Busy: 28 | <%=h @call.busy %> 29 |

30 | 31 |

32 | Seconds: 33 | <%=h @call.seconds %> 34 |

35 | 36 |

37 | Ringtime: 38 | <%=h @call.ringtime %> 39 |

40 | 41 |

42 | Rawfile: 43 | <%=h @call.rawfile %> 44 |

45 | 46 | 47 | <%= link_to 'Edit', edit_call_path(@project, @call) %> | 48 | <%= link_to 'Back', calls_path(@project) %> 49 | -------------------------------------------------------------------------------- /app/views/home/about.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |

About WarVOX

6 | 7 | WarVOX is a product of Rapid7 and is provided as 8 | free software. WarVOX is intended for legal security assessment, asset inventory, 9 | and research purposes only. The latest version of WarVOX can be found in GitHub at the following url 10 | https://github.com/rapid7/warvox/. The primary developer of WarVOX is Rapid7. 11 | 12 |
13 |
14 | 15 |

Statistics

16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 |
21 | Version 22 | <%= WarVOX::VERSION %>
29 | Projects 30 | <%= Project.count %>
36 | Providers 37 | <%= Provider.count %>
44 | Active Jobs 45 | <%= Job.where(:completed_at => nil).count %>
51 | Total Jobs 52 | <%= Job.count %>
58 | Calls 59 | <%= Call.count %>
63 |
64 |
65 | 66 | 67 | 68 |
69 |
70 | 71 | 72 |

Configuration

73 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 117 | 118 | 119 | 120 | 121 | 124 | 125 | 126 |
79 | Base Directory 80 | <%= WarVOX::Base %>
87 | Configuration File 88 | <%= WarVOX::Conf %>
94 | GNUPlot 95 | <%= WarVOX::Config.tool_path('gnuplot') || "MISSING" %>
101 | LAME 102 | <%= WarVOX::Config.tool_path('lame') || "MISSING" %>
108 | IAXRECORD 109 | <%= WarVOX::Config.tool_path('iaxrecord') || "MISSING" %>
115 | SOX 116 | <%= WarVOX::Config.tool_path('sox') || "MISSING" %>
122 | KissFFT 123 | <%= @has_kissfft %>
127 | 128 |

129 | 130 |

Dial Exclusions (Blacklist)

131 |
<%=h File.read(WarVOX::Config.blacklist_path) %>
132 | 133 |
134 |
135 | -------------------------------------------------------------------------------- /app/views/home/check.html.erb: -------------------------------------------------------------------------------- 1 | 2 |

Getting Started Checklist

3 | 4 | 11 | 12 |
13 | 14 |
1. Create a project to encapsulate your work
15 |
16 | <% if @has_project %> 17 |
18 | Looks good! 19 |

You have created a new project.

20 |
21 | <% else %> 22 |
23 | Woops! 24 |

You need to create a project first.

25 |
26 | <% end %> 27 |
28 | 29 | 30 |
2. Configure at least one VoIP Provider
31 |
32 | <% if @has_provider %> 33 |
34 | Perfect! 35 |

You have a VoIP provider configured.

36 |
37 | <% else %> 38 |
39 | This is the hard one 40 |

You need to configure a valid VoIP provider first.

41 |
42 | <% end %> 43 |
44 | 45 | 46 |
3. Dial a list of telephone numbers
47 |
48 | <% if @has_job %> 49 |
50 | Congratulations! 51 |

You have already launched a dialer job.

52 |
53 | <% else %> 54 |
55 | Ring Ring! 56 | <% if @has_project %> 57 |

You need to dial your target range to gather audio data.

58 | <% else %> 59 |

You need to create a project.

60 | <% end %> 61 |
62 | <% end %> 63 |
64 | 65 | 66 |
4. Gather an audio sample from at least one line
67 |
68 | <% if @has_result %> 69 |
70 | Woohoo! 71 |

You have already captured some audio data.

72 |
73 | <% else %> 74 |
75 | Sad Trombone! 76 | <% if @has_project %> 77 |

You need to dial some numbers that answer. 78 | Double check your VoIP settings and make sure the target numbers are picking up.

79 | <% else %> 80 |

You need to create a project first.

81 | <% end %> 82 |
83 | <% end %> 84 |
85 | 86 | 87 |
5. Analyze captured audio samples and extract signatures
88 |
89 | <% if @has_analysis %> 90 |
91 | Dayum! 92 |

You are good to go.

93 |
94 | <% else %> 95 |
96 | Crunch Time 97 | <% if @has_project and @has_result %> 98 |

Access the Analysis tab and start crunching audio samples.

99 | <% else %> 100 |

You need to create a project and successfully dial some numbers first.

101 | <% end %> 102 |
103 | <% end %> 104 |
105 | -------------------------------------------------------------------------------- /app/views/jobs/_view_results.json.erb: -------------------------------------------------------------------------------- 1 | { 2 | "sEcho": <%= echo_data_tables %>, 3 | "iTotalRecords": <%= @results_total_count.to_json %>, 4 | "iTotalDisplayRecords": <%= @results_total_display_count.to_json %>, 5 | "aaData": [ 6 | <% @results.each_with_index do |result, index| -%> 7 | { 8 | "DT_RowId": <%= dom_id(result).to_json.html_safe%>, 9 | "checkbox": <%= call_checkbox_tag(result) %>, 10 | "number": <%= call_number_html(result) %>, 11 | "caller_id": <%= call_caller_id_html(result) %>, 12 | "provider": <%= call_provider_html(result) %>, 13 | "answered": <%= call_answered_html(result) %>, 14 | "busy": <%= call_busy_html(result) %>, 15 | "audio_length": <%= call_audio_length_html(result) %>, 16 | "ring_length": <%= call_audio_length_html(result) %> 17 | }<%= ',' unless index == (@results.size - 1) %> 18 | <% end -%> 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /app/views/jobs/new.html.erb: -------------------------------------------------------------------------------- 1 |

Submit a New Job

2 | 3 | <%= form_for(@job, :html => { :multipart => true }) do |f| %> 4 | <%= f.error_messages %> 5 |

6 | <%= f.label :range, 'Specify target telephone range(s) (1-123-456-7890 or 1-123-456-XXXX or 1-123-300-1000:1-123-400-2000)' %>
7 | <%= f.text_area :range, :size => "35x5" %> 8 |

9 | 10 |

11 | <%= f.label :range_file, 'Or upload a file containing the target ranges' %>
12 | <%= f.file_field :range_file %> 13 |

14 | 15 |

16 | <%= f.label :seconds, 'Seconds of audio to capture' %>
17 | <%= f.text_field :seconds, :value => 53 %> 18 |

19 | 20 |

21 | <%= f.label :lines, 'Maximum number of outgoing lines' %>
22 | <%= f.text_field :lines, :value => 10 %> 23 |

24 | 25 |

26 | <%= f.label :lines, 'The source Caller ID range (1-555-555-55XX or SELF)' %>
27 | <%= f.text_field :cid_mask, :value => '1-123-456-XXXX' %> 28 |

29 | 30 |

31 | <%= f.submit "Create" %> 32 |

33 | <% end %> 34 | 35 | <%= link_to 'Back', jobs_path %> 36 | -------------------------------------------------------------------------------- /app/views/jobs/new_analyze.html.erb: -------------------------------------------------------------------------------- 1 |

Call Analysis

2 | 3 | <%= semantic_form_for(@job, :url => analyze_project_job_path, :html => { :multipart => true, :method => :put }) do |f| %> 4 | 5 | <% if @project %> 6 | <%= f.input :project_id, :as => :hidden, :input_html => { :value => @project.id } %> 7 | <% else %> 8 | <%= f.input :project, :as => :select %> 9 | <% end %> 10 | 11 | <%= f.input :force, :as => :boolean, :label => 'Process previously analyzed calls?', :input_html => { :value => 0 } %> 12 | <%= f.action :submit, :label => 'Analyze', :button_html => { :class => 'btn btn-large fbtn' } %> 13 | 14 | Cancel 15 | <% end %> 16 | 17 | <%= set_focus('job_submit') %> 18 | -------------------------------------------------------------------------------- /app/views/jobs/new_dialer.html.erb: -------------------------------------------------------------------------------- 1 |

Wardial Configuration

2 | 3 | <%= semantic_form_for(@job, :url => dialer_job_path, :html => { :multipart => true, :method => :put }) do |f| %> 4 | 5 | <% if @project %> 6 | <%= f.input :project_id, :as => :hidden, :input_html => { :value => @project.id } %> 7 | <% else %> 8 | <%= f.input :project, :as => :select %> 9 | <% end %> 10 | 11 | <%= f.input :range, :as => :text, 12 | :label => 'Target telephone range(s)', 13 | :hint => 'Examples: 1-123-456-7890 / 1-123-456-XXXX / 1-123-300-1000:1-123-400-2000', 14 | :input_html => { :rows => 3, :cols => 80, :autofocus => true } 15 | %> 16 | <%= f.input :range_file, :as => :file, :label => 'Or upload a file containing the target ranges' %> 17 | <%= f.input :seconds, :as => :number, :label => 'Seconds of audio to capture', :input_html => { :value => 53 } %> 18 | <%= f.input :lines, :as => :number, :label => 'Maximum number of outgoing lines', :input_html => { :value => 10 } %> 19 | <%= f.input :cid_mask, :as => :string, :label => 'The source Caller ID range (1-555-555-55XX or SELF)', :input_html => { :value => '1-555-555-XXXX' } %> 20 | 21 | <%= f.action :submit, :label => 'Dial', :button_html => { :class => 'btn btn-large fbtn' } %> 22 | 23 | Cancel 24 | <% end %> 25 | 26 | <%= set_focus('job_range') %> 27 | -------------------------------------------------------------------------------- /app/views/jobs/results.html.erb: -------------------------------------------------------------------------------- 1 | <% if @jobs.length > 0 %> 2 |

Scan Results

3 | 4 | <%= will_paginate @jobs, :renderer => BootstrapPagination::Rails %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% @jobs.each do |job| 21 | 22 | cnt_dialed = job.calls.count.to_i 23 | cnt_answered = job.calls.where("answered = ? and busy = ?", true, false).count.to_i 24 | cnt_analyzed = job.calls.where("analysis_completed_at IS NOT NULL").count.to_i 25 | pct_answered = 0 26 | pct_analyzed = 0 27 | unless cnt_dialed == 0 28 | pct_answered = ((cnt_answered.to_f / cnt_dialed.to_f) * 100).to_i 29 | end 30 | unless cnt_answered == 0 31 | pct_analyzed = ((cnt_analyzed.to_f / cnt_answered.to_f) * 100).to_i 32 | end 33 | %> 34 | 35 | 36 | 37 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 72 | 73 | 74 | <% end %> 75 | 76 |
IDTypeDetailsAnsweredAnalyzedLaunchedUserActions
<%= job.id %><%= format_job_details(job) %> 38 | <% if job.task == "dialer" %> 39 | <%= truncate(job.details[:range].to_s, :length => 15) %> / 40 | <%= job.details[:cid_mask].to_s %> 41 | <% end %> 42 | 43 | <%= truncate(job.details[:directory].to_s, :length => 15) %> 44 | <% if job.task == "importer" %> 45 | 46 | <% end %> 47 | <%= number_with_delimiter(cnt_answered) %> / <%= number_with_delimiter(cnt_dialed) %><%= number_with_delimiter(cnt_analyzed) %><%= time_ago_in_words(job.created_at) %> ago<%= job.created_by %> 55 | 56 | 57 | <% if cnt_analyzed > 0 %> 58 | 59 | <% if pct_analyzed == 100 %> 60 | 61 | <% else %> 62 | 63 | <% end %> 64 | <% else %> 65 | <% if cnt_answered > 0 %> 66 | 67 | <% end %> 68 | <% end %> 69 | 70 | 71 |
77 | 78 | <%= will_paginate @jobs, :renderer => BootstrapPagination::Rails %> 79 | 80 | <% else %> 81 | 82 |

No Completed Jobs

83 |
84 | 85 | <% end %> 86 | 87 | Wardial 88 | -------------------------------------------------------------------------------- /app/views/jobs/run.html.erb: -------------------------------------------------------------------------------- 1 |

Run Job

2 | 3 | Running this job...
4 | 5 | <%= link_to 'Back', jobs_path %> 6 | -------------------------------------------------------------------------------- /app/views/jobs/view_results.html.erb: -------------------------------------------------------------------------------- 1 | <% include_view_javascript %> 2 | 3 |

Call Results for Scan #<%=@job.id%>

4 | 5 | 6 | 7 | 10 | 11 |
8 | <%= render :partial => 'shared/graphs/call_results' %> 9 |
12 | 13 | 14 | <%= form_tag do %> 15 |
16 | 17 | 18 | 25 |
19 | <%= submit_checkboxes_to(raw(' Scan'), new_dialer_job_path, { :class => "btn btn any" }) %> 20 | 21 | <%= submit_checkboxes_to(raw(' Analyze'), analyze_job_path, { :class => "btn btn any" }) %> 22 | 23 | <%= submit_checkboxes_to(raw(' Delete'), purge_calls_job_path, { :class => "btn any", :confirm => 'Purge selected calls?' }) %> 24 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
<%= check_box_tag "all_results", false %>NumberSource CIDProviderAnsweredBusySecondsRing Time
50 | 51 | <% end %> 52 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= content_for?(:title) ? yield(:title) : "WarVOX v#{WarVOX::VERSION}" %> 8 | <%= csrf_meta_tags %> 9 | 10 | 13 | 14 | <%= javascript_include_tag "application" %> 15 | 16 | <%= yield :view_javascript %> 17 | <%= stylesheet_link_tag "application", :media => "all" %> 18 | 19 | <%= yield :view_stylesheets %> 20 | <%= favicon_link_tag '/assets/favicon.ico', :rel => 'shortcut icon' %> 21 | <%= javascript_tag do %> 22 | $(document).ready(function() { 23 | 24 | $(".xtooltip").tooltip(); 25 | 26 | $(".xpopover").popover({ 27 | html: true, 28 | placement: 'right', 29 | trigger: 'hover', 30 | delay: { show: 300, hide: 300 }, 31 | animation: false, 32 | }); 33 | }); 34 | <% end %> 35 | 36 | 37 | <%= render 'nav' %> 38 |
39 |
40 |
41 |
42 | 43 |

<%= flash[:notice] %>

44 | <%= yield %> 45 |
46 | 47 |
48 |

WarVOX v<%=WarVOX::VERSION %> © 2009-<%= Time.now.year %> Rapid7, Inc

49 |
50 | 51 |
52 |
53 |
54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/views/layouts/login.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= content_for?(:title) ? yield(:title) : "WarVOX v#{WarVOX::VERSION}" %> 8 | <%= csrf_meta_tags %> 9 | 10 | 13 | 14 | <%= javascript_include_tag "application" %> 15 | <%= stylesheet_link_tag "application", :media => "all" %> 16 | <%= favicon_link_tag '/assets/favicon.ico', :rel => 'shortcut icon' %> 17 | 18 | <%= javascript_tag do %> 19 | $(document).ready(function() { 20 | $("a").tooltip(); 21 | }); 22 | <% end %> 23 | 24 | 25 |
26 | 29 |
30 | <%= yield %> 31 |
32 | 33 |
34 |

WarVOX v<%=WarVOX::VERSION %> © 2009-<%= Time.now.year%> Rapid7, Inc

35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/views/projects/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Update Project

2 | 3 | <%= semantic_form_for(@project) do |f| %> 4 | <%= f.input :name, :as => :string, :label => 'Name' %> 5 | <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %> 6 | <%= f.action :submit, :label => 'Update', :button_html => { :class => 'btn btn-large fbtn' } %> 7 | Cancel 8 | <% end %> 9 | 10 | <%= set_focus('project_name') %> 11 | -------------------------------------------------------------------------------- /app/views/projects/index.html.erb: -------------------------------------------------------------------------------- 1 |

WarVOX Projects

2 |

3 | 4 | Create Project 5 | 6 |

7 | 8 | <%= will_paginate @projects, :renderer => BootstrapPagination::Rails %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% @projects.each do |project| %> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | <% end %> 36 | 37 |
NameDescriptionJobsCallsAnalyzedCreatedActions
<%= link_to( h(project.name), project_path(project)) %><%=truncate(project.description, :length => 40, :separator => '') %><%= number_with_delimiter(project.jobs.count) %><%= number_with_delimiter(project.calls.count) %><%= number_with_delimiter(project.calls.where('analysis_completed_at IS NOT NULL').count) %><%= time_ago_in_words(project.created_at) %> ago 31 | 32 | 33 |
38 | 39 | <%= will_paginate @projects, :renderer => BootstrapPagination::Rails %> 40 | 41 | -------------------------------------------------------------------------------- /app/views/projects/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Project

2 | 3 | <%= semantic_form_for(@new_project) do |f| %> 4 | <%= f.input :name, :as => :string, :label => 'Name', :input_html => { :autofocus => true } %> 5 | <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %> 6 | <%= f.action :submit, :label => 'Create', :button_html => { :class => 'btn btn-large fbtn' } %> 7 | Cancel 8 | <% end %> 9 | 10 | <%= set_focus('project_name') %> 11 | -------------------------------------------------------------------------------- /app/views/providers/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Update Provider

2 | 3 | <%= semantic_form_for(@provider) do |f| %> 4 | <%= f.input :enabled, :as => :boolean %> 5 | <%= f.input :name, :as => :string, :label => 'Name', :hint => 'A friendly name for this provider', :autofocus => true %> 6 | <%= f.input :host, :as => :string, :label => 'IAX2 Server', :hint => 'The IP address or hostname of the IAX2 service' %> 7 | <%= f.input :port, :as => :number, :label => 'IAX2 Port', :hint => 'The port of the IAX2 service' %> 8 | <%= f.input :user, :as => :string, :label => 'Username' %> 9 | <%= f.input :pass, :as => :string, :label => 'Password', :input_html => { :autocomplete => false } %> 10 | <%= f.input :lines, :as => :number, :label => 'Maximum Lines', :hint => 'Maximum concurrent outbound lines' %> 11 | 12 | <%= f.action :submit, :label => 'Update', :button_html => { :class => 'btn btn-large fbtn' } %> 13 | Cancel 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/providers/index.html.erb: -------------------------------------------------------------------------------- 1 | <% if @providers.length > 0 %> 2 |

VoIP Providers (IAX)

3 | 4 | <%= will_paginate @providers, :renderer => BootstrapPagination::Rails %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% @providers.each do |provider| %> 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | <% end %> 33 |
ActiveProvider NameIAX2 ServerIAX2 PortUsernameLinesActions
20 | "> 21 | <%= provider.name %><%= provider.host %><%= provider.port %><%= provider.user %><%= provider.lines %> 28 | 29 | 30 |
34 | <%= will_paginate @providers, :renderer => BootstrapPagination::Rails %> 35 | <% else %> 36 |

No Configured Providers

37 | <% end %> 38 | 39 |
40 | Add Provider 41 | -------------------------------------------------------------------------------- /app/views/providers/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Provider

2 | 3 | <%= semantic_form_for(@provider) do |f| %> 4 | <%= f.input :name, :as => :string, :label => 'Name', :hint => 'A friendly name for this provider', :autofocus => true %> 5 | <%= f.input :host, :as => :string, :label => 'IAX2 Server', :hint => 'The IP address or hostname of the IAX2 service' %> 6 | <%= f.input :port, :as => :number, :label => 'IAX2 Port', :hint => 'The port of the IAX2 service' %> 7 | <%= f.input :user, :as => :string, :label => 'Username' %> 8 | <%= f.input :pass, :as => :password, :label => 'Password' %> 9 | <%= f.input :lines, :as => :number, :label => 'Maximum Lines', :hint => 'Maximum concurrent outbound lines' %> 10 | 11 | <%= f.action :submit, :label => 'Create', :button_html => { :class => 'btn btn-large fbtn' } %> 12 | Cancel 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/settings/index.html.erb: -------------------------------------------------------------------------------- 1 | <% Settings.all.each do |s| %> 2 | <%= s.inspect %>
3 | 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/shared/_call_info.html.erb: -------------------------------------------------------------------------------- 1 | 2 | " width="17" height="17"> 3 | " /> 4 | 5 | 6 | <%= call.number %> 7 | -------------------------------------------------------------------------------- /app/views/shared/_call_signal.html.erb: -------------------------------------------------------------------------------- 1 | <%= render :partial => 'shared/lightbox_sig', :locals => { :call => call } %> 2 | <%= render :partial => 'shared/lightbox_freq', :locals => { :call => call } %> 3 | 4 | <% if call.fprint and call.fprint.length > 0 and call != @result %> 5 | <% if @job_id %> 6 | 7 | <% else %> 8 | 9 | <% end%> 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/shared/_call_type.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | ttip = raw("
") 3 | ttip << raw("
Call Time:
") 4 | ttip << raw("
") + h(call.created_at.strftime("%Y-%m-%d %H:%M:%S %Z")) + raw(" 
") 5 | ttip << raw("
CallerID:
") 6 | ttip << raw("
") + h(call.caller_id) + raw(" 
") 7 | ttip << raw("
Provider:
") 8 | ttip << raw("
") + h(call.provider.name) + raw(" 
") 9 | ttip << raw("
Audio:
") 10 | ttip << raw("
") + h(call.audio_length.to_s) + raw(" 
") 11 | ttip << raw("
Ring:
") 12 | ttip << raw("
") + h(call.ring_length.to_s) + raw(" 
") 13 | ttip << raw("
\n") 14 | %><%= raw("#{h call.line_type.to_s.upcase }") -%> 15 | -------------------------------------------------------------------------------- /app/views/shared/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /app/views/shared/_header.html.erb: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/views/shared/_lightbox_freq.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | lid = "call_#{call.id}_freq" 3 | %> 4 | 9 | 10 | "/> 11 | -------------------------------------------------------------------------------- /app/views/shared/_lightbox_sig.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | lid = "call_#{call.id}_sig" 3 | %> 4 | 9 | 10 | " /> 11 | -------------------------------------------------------------------------------- /app/views/shared/graphs/_call_results.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | 3 | graph_id = "g" + rand(0x1000000).to_s 4 | line_data = raw("") 5 | 6 | line_data << raw("{ name: 'Timeout', data: [ #{@call_results[:Timeout].to_i.to_s } ] }, ") 7 | line_data << raw("{ name: 'Busy', data: [ #{@call_results[:Busy].to_i.to_s } ] }, ") 8 | line_data << raw("{ name: 'Answered', data: [ #{@call_results[:Answered].to_i.to_s } ] }, ") 9 | 10 | %> 11 | 12 |
13 | 14 | <%= javascript_tag do %> 15 | $(document).ready(function() { 16 | chart = new Highcharts.Chart({ 17 | chart: { 18 | renderTo: '<%= graph_id %>', 19 | type: 'bar', 20 | height: 150 21 | }, 22 | title: { 23 | text: 'Call Results' 24 | }, 25 | xAxis: { 26 | categories: ['Calls'] 27 | }, 28 | yAxis: { 29 | min: 0, 30 | title: { 31 | text: 'Calls by Result' 32 | } 33 | }, 34 | legend: { 35 | backgroundColor: '#FFFFFF', 36 | reversed: true 37 | }, 38 | tooltip: { 39 | formatter: function() { 40 | return ''+ 41 | this.series.name +': '+ this.y +''; 42 | } 43 | }, 44 | plotOptions: { 45 | series: { 46 | stacking: 'normal', 47 | animation : false 48 | } 49 | }, 50 | series: [ <%= line_data %> ] 51 | }); 52 | }); 53 | <% end %> 54 | -------------------------------------------------------------------------------- /app/views/shared/graphs/_lines_by_type.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | 3 | graph_id = "g" + rand(0x1000000).to_s 4 | line_data = raw("") 5 | 6 | @lines_by_type.keys.sort{|a,b| @lines_by_type[b] <=> @lines_by_type[a]}.each do |k| 7 | line_data << raw("{ name: '#{k.to_s.capitalize}', data: [ #{ @lines_by_type[k].to_i.to_s} ]}, ") 8 | end 9 | 10 | %> 11 | 12 |
13 | 14 | <%= javascript_tag do %> 15 | 16 | $(document).ready(function() { 17 | chart = new Highcharts.Chart({ 18 | chart: { 19 | renderTo: '<%= graph_id %>', 20 | type: 'bar', 21 | height: 250 22 | }, 23 | title: { 24 | text: 'Line Types' 25 | }, 26 | xAxis: { 27 | categories: ['Lines'] 28 | }, 29 | yAxis: { 30 | min: 0, 31 | title: { 32 | text: 'Lines by Type' 33 | } 34 | }, 35 | legend: { 36 | backgroundColor: '#FFFFFF', 37 | reversed: true 38 | }, 39 | tooltip: { 40 | formatter: function() { 41 | return ''+ 42 | this.series.name +': '+ this.y +''; 43 | } 44 | }, 45 | plotOptions: { 46 | series: { 47 | stacking: 'normal', 48 | animation : false 49 | } 50 | }, 51 | series: [ <%= line_data %> ] 52 | }); 53 | }); 54 | 55 | <% end %> 56 | -------------------------------------------------------------------------------- /app/views/shared/graphs/_sparkline.html.erb: -------------------------------------------------------------------------------- 1 | <% graph_id = "g" + rand(0x10000000).to_s %> 2 | 3 |
4 | 5 | <%= javascript_tag do %> 6 | $(document).ready(function() { 7 | var chart = new Highcharts.Chart({ 8 | chart: { 9 | renderTo: '<%= graph_id %>', 10 | defaultSeriesType: 'area', 11 | margin:[0,0,0,0], 12 | // borderWidth: 0; 13 | }, 14 | title:{ 15 | text:'' 16 | }, 17 | credits:{ 18 | enabled:false 19 | }, 20 | xAxis: { 21 | labels: { 22 | enabled:false 23 | } 24 | }, 25 | yAxis: { 26 | maxPadding:0, 27 | minPadding:0, 28 | endOnTick:false, 29 | labels: { 30 | enabled:false 31 | } 32 | }, 33 | legend: { 34 | enabled:false 35 | }, 36 | tooltip: { 37 | enabled:false 38 | }, 39 | plotOptions: { 40 | series:{ 41 | lineWidth:1, 42 | shadow:false, 43 | states:{ 44 | hover:{ 45 | lineWidth:1 46 | } 47 | }, 48 | marker:{ 49 | //enabled:false, 50 | radius:1, 51 | states:{ 52 | hover:{ 53 | radius:2 54 | } 55 | } 56 | } 57 | } 58 | }, 59 | series: [{ 60 | color:'#BB4607', 61 | fillColor:'#fcfcfc', 62 | data: [ <%= raw(points.join(", ")) %> ] 63 | }] 64 | }); 65 | }); 66 | 67 | <% end %> 68 | -------------------------------------------------------------------------------- /app/views/user_sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | 4 | 5 | <%= form_for @user_session do |f| %> 6 | <% if @user_session.errors.any? %> 7 |
<%= @user_session.errors.full_messages.first %>
8 | <% end %> 9 |

<%= f.text_field :login, :spellcheck => false, :autofocus => true %>

10 |

<%= f.password_field :password, :autocomplete => 'off' %>

11 | <%= f.submit "Sign in", :class => "btn-login btn btn-warning" %> 12 | <% end %> 13 | <%= set_focus('user_session_login') %> 14 | -------------------------------------------------------------------------------- /app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form.label :login %>
2 | <%= form.text_field :login %>
3 |
4 | <%= form.label :password, form.object.new_record? ? nil : "Change password" %>
5 | <%= form.password_field :password %>
6 |
7 | <%= form.label :password_confirmation %>
8 | <%= form.password_field :password_confirmation %>
9 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit My Account

2 | 3 | <%= form_for @user, :url => user_path(@user) do |f| %> 4 | <%= f.error_messages %> 5 | <%= render :partial => "form", :object => f %> 6 | <%= f.submit "Update" %> 7 | <% end %> 8 | 9 |
<%= link_to "My Profile", user_path(@user) %> 10 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 |

Register

2 | 3 | <% form_for @user, :url => account_path do |f| %> 4 | <%= f.error_messages %> 5 | <%= render :partial => "form", :object => f %> 6 | <%= f.submit "Register" %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

2 | Login: 3 | <%=h @user.login %> 4 |

5 | 6 |

7 | Login count: 8 | <%=h @user.login_count %> 9 |

10 | 11 |

12 | Last request at: 13 | <%=h @user.last_request_at %> 14 |

15 | 16 |

17 | Last login at: 18 | <%=h @user.last_login_at %> 19 |

20 | 21 |

22 | Current login at: 23 | <%=h @user.current_login_at %> 24 |

25 | 26 |

27 | Last login ip: 28 | <%=h @user.last_login_ip %> 29 |

30 | 31 |

32 | Current login ip: 33 | <%=h @user.current_login_ip %> 34 |

35 | 36 | 37 | <%= link_to 'Edit', edit_user_path %> 38 | -------------------------------------------------------------------------------- /bin/adduser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV['RAILS_ENV'] ||= 'production' 4 | 5 | # bundler/setup just sets up the $LOAD_PATHs, the gems aren't automatically required... 6 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile',File.dirname(__FILE__)) 7 | require 'bundler/setup' 8 | 9 | # rails/all must be required explicitly to get the railties that pro/ui/config/application.rb uses 10 | require 'rails/all' 11 | # require all the gems in the current environment 12 | Bundler.require(*Rails.groups(assets: %w(development test cucumber))) 13 | 14 | APP_PATH = File.expand_path('../../config/application', __FILE__) 15 | require File.expand_path('../../config/boot', __FILE__) 16 | require APP_PATH 17 | Rails.application.require_environment! 18 | 19 | def generate_password 20 | set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*") 21 | set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 22 | str = '' 23 | cnt = 0 24 | 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]/) 25 | if str.length > 12 26 | str = str[0,4] 27 | next 28 | end 29 | str << set[ rand(set.length), 1] 30 | cnt += 1 31 | end 32 | str 33 | end 34 | 35 | 36 | username = ARGV.shift 37 | password = ARGV.shift 38 | 39 | user = username ? User.find_by_login(username) : nil 40 | 41 | if not user 42 | 43 | if ! username 44 | $stdout.write "[*] Please enter a username: " 45 | $stdout.flush 46 | username = $stdin.readline.strip 47 | end 48 | 49 | if ! (username and username.strip.length > 0) 50 | $stdout.puts "[-] Invalid username specified" 51 | exit(0) 52 | end 53 | 54 | if not password 55 | randpass = generate_password 56 | $stdout.puts "" 57 | $stdout.puts "[*] Creating user '#{username}' with password '#{randpass}' ..." 58 | $stdout.puts "" 59 | password = randpass 60 | end 61 | 62 | user = User.new() 63 | user.login = username 64 | user.password = password 65 | user.password_confirmation = password 66 | user.save! 67 | 68 | $stdout.puts "[*] User #{user.login} has been created, please change your password on login." 69 | else 70 | $stdout.puts "[*] That user account already exists, please try 'resetpw' script" 71 | end 72 | -------------------------------------------------------------------------------- /bin/analyze_result.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | require 'warvox' 13 | 14 | # 15 | # Script 16 | # 17 | 18 | inp = ARGV.shift || exit(0) 19 | num = ARGV.shift || exit(0) 20 | 21 | $0 = "warvox(analyzer): #{inp} #{num}" 22 | 23 | begin 24 | 25 | $stdout.write( 26 | Marshal.dump( 27 | WarVOX::Jobs::Analysis.analyze_call( 28 | inp, num 29 | ) 30 | ) 31 | ) 32 | 33 | rescue ::Errno::EPIPE 34 | # Hide pipe errors (parent is killed when task was cancelled) 35 | end 36 | -------------------------------------------------------------------------------- /bin/audio_compare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'pry' 15 | 16 | def usage 17 | $stderr.puts "Usage: #{$0} " 18 | exit 19 | end 20 | 21 | def log(m) 22 | $stderr.puts "[*] #{m}" 23 | end 24 | 25 | def score(a,b) 26 | (a & b).length / [a,b].max.length.to_f 27 | end 28 | 29 | # 30 | # Script 31 | # 32 | 33 | inp1 = ARGV.shift 34 | inp2 = ARGV.shift 35 | 36 | if [inp1, inp2].include?("-h") or not (inp1 && inp2) 37 | usage() 38 | end 39 | 40 | # log("Processing #{inp1}...") 41 | raw1 = WarVOX::Audio::Raw.from_file(inp1) 42 | sig1 = raw1.to_freq_sig 43 | 44 | # log("Processing #{inp2}...") 45 | raw2 = WarVOX::Audio::Raw.from_file(inp2) 46 | sig2 = raw2.to_freq_sig 47 | 48 | puts "Score: #{score(sig1, sig2)}" 49 | -------------------------------------------------------------------------------- /bin/audio_raw_to_flac.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | def usage 16 | $stderr.puts "Usage: #{$0} " 17 | exit 18 | end 19 | 20 | # 21 | # Script 22 | # 23 | 24 | inp = ARGV.shift 25 | out = ARGV.shift 26 | 27 | if (inp and inp == "-h") or not inp 28 | usage() 29 | end 30 | 31 | raw = WarVOX::Audio::Raw.from_file(inp) 32 | if out 33 | ::File.open(out, "wb") do |fd| 34 | fd.write(raw.to_flac) 35 | end 36 | else 37 | $stdout.write(raw.to_flac) 38 | end 39 | -------------------------------------------------------------------------------- /bin/audio_raw_to_fprint.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | def usage 16 | $stderr.puts "Usage: #{$0} " 17 | exit 18 | end 19 | 20 | # 21 | # Script 22 | # 23 | 24 | inp = ARGV.shift 25 | out = ARGV.shift 26 | 27 | if (inp and inp == "-h") or not inp 28 | usage() 29 | end 30 | 31 | raw = WarVOX::Audio::Raw.from_file(inp) 32 | if out 33 | ::File.open(out, "wb") do |fd| 34 | fd.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}" ) 35 | end 36 | else 37 | $stdout.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}" ) 38 | end 39 | -------------------------------------------------------------------------------- /bin/audio_raw_to_speech.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | require 'uri' 16 | require 'net/http' 17 | require 'json' 18 | 19 | def usage 20 | $stderr.puts "Usage: #{$0} " 21 | exit 22 | end 23 | 24 | # 25 | # Script 26 | # 27 | 28 | inp = ARGV.shift 29 | out = ARGV.shift 30 | 31 | if (inp and inp == "-h") or not inp 32 | usage() 33 | end 34 | 35 | if out && File.exists?(out) 36 | $stderr.puts "Error: The output file already exists: #{out}" 37 | exit(0) 38 | end 39 | 40 | raw = WarVOX::Audio::Raw.from_file(inp) 41 | res = nil 42 | flac = raw.to_flac 43 | akey = WarVOX::Config.gcloud_key 44 | 45 | if ! akey 46 | $stderr.puts "Error: A gcloud API key needs to be configured" 47 | exit(1) 48 | end 49 | 50 | uri = URI('https://speech.googleapis.com/v1/speech:recognize?key=' + akey) 51 | req = Net::HTTP::Post.new(uri, initheader = {'Content-Type' =>'application/json'}) 52 | 53 | loop do 54 | req.body = 55 | { 56 | "initialRequest" => { 57 | "encoding" => "FLAC", 58 | "sampleRate" => 16000, 59 | }, 60 | "audioRequest" => { 61 | "content" => [flac].pack("m*").gsub(/\s+/, '') 62 | } 63 | }.to_json 64 | 65 | begin 66 | http = Net::HTTP.new(uri.hostname, uri.port) 67 | http.use_ssl = true 68 | res = http.request(req) 69 | 70 | break if res.code.to_s == "200" 71 | $stderr.puts "Retrying due to #{res.code} #{res.message}..." 72 | rescue ::Interrupt 73 | exit(0) 74 | rescue ::Exception 75 | $stderr.puts "Exception: #{$!} #{$!.backtrace}" 76 | end 77 | sleep(1) 78 | end 79 | 80 | if out 81 | ::File.open(out, "wb") do |fd| 82 | fd.write(res.body) 83 | end 84 | else 85 | $stdout.write(res.body) 86 | end 87 | -------------------------------------------------------------------------------- /bin/audio_raw_to_wav.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | def usage 16 | $stderr.puts "Usage: #{$0} " 17 | exit 18 | end 19 | 20 | # 21 | # Script 22 | # 23 | 24 | inp = ARGV.shift 25 | out = ARGV.shift 26 | 27 | if (inp and inp == "-h") or not inp 28 | usage() 29 | end 30 | 31 | raw = WarVOX::Audio::Raw.from_file(inp) 32 | if out 33 | ::File.open(out, "wb") do |fd| 34 | fd.write(raw.to_wav) 35 | end 36 | else 37 | $stdout.write(raw.to_wav) 38 | end 39 | -------------------------------------------------------------------------------- /bin/audio_trim.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | def usage 16 | $stderr.puts "Usage: #{$0} [offset] [length] " 17 | exit 18 | end 19 | 20 | # TODO: Needs WAV header support 21 | 22 | # 23 | # Script 24 | # 25 | 26 | off = ARGV.shift 27 | len = ARGV.shift 28 | inp = ARGV.shift 29 | out = ARGV.shift 30 | 31 | if (off and off == "-h") or not off 32 | usage() 33 | end 34 | 35 | buf = '' 36 | ifd = nil 37 | 38 | if inp 39 | ifd = ::File.open(inp, "rb") 40 | else 41 | $stdin.binmode 42 | ifd = $stdin 43 | end 44 | 45 | ofd = nil 46 | 47 | if out 48 | ofd = ::File.open(out, "wb") 49 | else 50 | $stdout.binmode 51 | ofd = $stdout 52 | end 53 | 54 | 55 | buf = ifd.read 56 | off = off.to_i * 16000 57 | len = (len.to_i > 0) ? len.to_i : (buf.length / 16000).to_i 58 | 59 | ofd.write( buf[off, len * 16000] ) 60 | exit(0) 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/cache_clear.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'fileutils' 15 | require 'yaml' 16 | 17 | ENV['RAILS_ENV'] ||= 'production' 18 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 19 | 20 | 21 | $stderr.puts "[*] Loading database environment..." 22 | 23 | require 'config/boot' 24 | require 'config/environment' 25 | 26 | 27 | $stderr.puts "[*] Clearing the report cache..." 28 | Saulabs::Reportable::ReportCache.delete_all 29 | -------------------------------------------------------------------------------- /bin/export_audio.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'fileutils' 15 | require 'yaml' 16 | 17 | ENV['RAILS_ENV'] ||= 'production' 18 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 19 | 20 | def usage 21 | $stderr.puts "Usage: #{$0} [Output Dir] [Project ID] " 22 | exit 23 | end 24 | 25 | # 26 | # Script 27 | # 28 | 29 | output = ARGV.shift 30 | project_id = ARGV.shift 31 | line_type = ARGV.shift 32 | 33 | if(output and output == "-h") or (! output) 34 | usage() 35 | end 36 | 37 | require 'config/boot' 38 | require 'config/environment' 39 | 40 | if project_id.to_i == 0 41 | $stderr.puts "Listing all projects" 42 | $stderr.puts "====================" 43 | Project.all.each do |j| 44 | puts "#{j.id}\t#{j.name}\t#{j.created_at}" 45 | end 46 | exit 47 | end 48 | 49 | FileUtils.mkdir_p(output) 50 | 51 | begin 52 | cond = { project_id: project_id.to_i, answered: true, busy: false } 53 | if line_type 54 | cond[:line_type] = line_type.downcase 55 | end 56 | 57 | Call.where(cond).order(number: :asc).each do |r| 58 | m = r.media 59 | if m and m.audio 60 | 61 | ::File.open(File.join(output, "#{r.number}.wav"), "wb") do |fd| 62 | if m.audio[0,4].to_s == "RIFF" 63 | fd.write(m.audio) 64 | else 65 | # Add a WAV header to the sample 66 | raw = WarVOX::Audio::Raw.new(m.audio) 67 | fd.write(raw.to_wav) 68 | end 69 | end 70 | 71 | ::File.open(File.join(output, "#{r.number}.yml"), "wb") do |fd| 72 | fd.write(r.to_yaml) 73 | end 74 | 75 | if m.mp3 76 | ::File.open(File.join(output, "#{r.number}.mp3"), "wb") do |fd| 77 | fd.write(m.mp3) 78 | end 79 | end 80 | 81 | if m.png_big 82 | ::File.open(File.join(output, "#{r.number}_wave.png"), "wb") do |fd| 83 | fd.write(m.png_big) 84 | end 85 | end 86 | 87 | if m.png_big_freq 88 | ::File.open(File.join(output, "#{r.number}_freq.png"), "wb") do |fd| 89 | fd.write(m.png_big_freq) 90 | end 91 | end 92 | 93 | $stderr.puts "[*] Exported #{r.number}..." 94 | 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /bin/export_list.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'csv' 15 | 16 | ENV['RAILS_ENV'] ||= 'production' 17 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 18 | 19 | def usage 20 | $stderr.puts "Usage: #{$0} [Job ID] " 21 | exit 22 | end 23 | 24 | # 25 | # Script 26 | # 27 | 28 | project_id = ARGV.shift 29 | line_type = ARGV.shift 30 | 31 | if(project_id and project_id == "-h") 32 | usage() 33 | end 34 | 35 | if project_id.to_i == 0 36 | usage() 37 | end 38 | 39 | require 'config/boot' 40 | require 'config/environment' 41 | 42 | if(not project_id) 43 | $stderr.puts "Listing all projects" 44 | $stderr.puts "====================" 45 | Project.all.each do |j| 46 | puts "#{j.id}\t#{j.name}\t#{j.created_at}" 47 | end 48 | exit 49 | end 50 | 51 | fields = %W{ number line_type caller_id answered busy audio_length ring_length peak_freq } 52 | begin 53 | $stdout.puts fields.to_csv 54 | cond = { project_id: project_id.to_i } 55 | if line_type 56 | cond[:line_type] = line_type.downcase 57 | end 58 | Call.where(cond).order(number: :asc).each do |r| 59 | out = [] 60 | fields.each do |f| 61 | out << r[f].to_s 62 | end 63 | $stdout.puts out.to_csv 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /bin/iaxrecord.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $:.unshift(::File.join(::File.dirname(__FILE__), "..", "lib")) 4 | 5 | 6 | def stop 7 | exit(0) 8 | end 9 | 10 | trap("SIGINT") { stop() } 11 | trap("SIGTERM") { stop() } 12 | 13 | require 'rubygems' 14 | require "warvox/proto/iax2" 15 | require "optparse" 16 | 17 | parser = OptionParser.new 18 | opts = { 19 | recording_time: 52 20 | } 21 | 22 | parser.banner = "Usage: #{$0} [options]" 23 | parser.on("-s server") do |v| 24 | opts[:server_host] = v 25 | end 26 | 27 | parser.on("-u user") do |v| 28 | opts[:username] = v 29 | end 30 | 31 | parser.on("-p pass") do |v| 32 | opts[:password] = v 33 | end 34 | 35 | parser.on("-o output") do |v| 36 | opts[:output] = v 37 | end 38 | 39 | parser.on("-n number") do |v| 40 | opts[:called_number] = v 41 | end 42 | 43 | parser.on("-c cid") do |v| 44 | opts[:caller_number] = v 45 | end 46 | 47 | parser.on("-l seconds") do |v| 48 | opts[:recording_time] = v.to_i 49 | end 50 | 51 | parser.on("-d") do |v| 52 | opts[:debugging] = true 53 | end 54 | 55 | parser.on("-k keys") do |v| 56 | opts[:sendkeys] = v 57 | end 58 | 59 | parser.on("-h") do 60 | $stderr.puts parser 61 | exit(1) 62 | end 63 | 64 | parser.parse!(ARGV) 65 | 66 | if not (opts[:server_host] and opts[:username] and opts[:password] and opts[:called_number] and opts[:output]) 67 | $stderr.puts parser 68 | exit(1) 69 | end 70 | 71 | 72 | cli = WarVOX::Proto::IAX2::Client.new(opts) 73 | 74 | reg = cli.create_call 75 | r = reg.register 76 | if not r 77 | $stderr.puts "ERROR: Unable to register with the IAX server" 78 | exit(0) 79 | end 80 | 81 | c = cli.create_call 82 | r = c.dial( opts[:called_number] ) 83 | if not r 84 | $stderr.puts "ERROR: Unable to dial the requested number" 85 | exit(0) 86 | end 87 | 88 | begin 89 | 90 | ::Timeout.timeout( opts[:recording_time] ) do 91 | while (c.state != :hangup) 92 | case c.state 93 | when :ringing 94 | when :answered 95 | when :hangup 96 | break 97 | end 98 | select(nil,nil,nil, 0.25) 99 | end 100 | end 101 | rescue ::Timeout::Error 102 | ensure 103 | c.hangup rescue nil 104 | end 105 | 106 | cli.shutdown 107 | 108 | cnt = 0 109 | fd = ::File.open( opts[:output], "wb") 110 | c.each_audio_frame do |frame| 111 | fd.write(frame) 112 | cnt += frame.length 113 | end 114 | fd.close 115 | 116 | $stdout.puts "COMPLETED: BYTES=#{cnt} RINGTIME=#{c.ring_time} FILE=#{ ::File.expand_path( opts[:output] ) } BUSY=#{c.busy ? 1 : 0} FAIL=#{cnt == 0 ? 1 : 0}" 117 | -------------------------------------------------------------------------------- /bin/identify_matches.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | 15 | ENV['RAILS_ENV'] ||= 'production' 16 | 17 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 18 | require 'config/boot' 19 | require 'config/environment' 20 | 21 | def usage 22 | $stderr.puts "Usage: #{$0} [job|all] " 23 | exit 24 | end 25 | 26 | # 27 | # Script 28 | # 29 | 30 | job = ARGV.shift 31 | fp = ARGV.shift 32 | 33 | if(job and job == "-h") 34 | usage() 35 | end 36 | 37 | if(not job) 38 | $stderr.puts "Listing all available jobs" 39 | $stderr.puts "==========================" 40 | DialJob.find(:all).each do |j| 41 | puts "#{j.id}\t#{j.started_at} --> #{j.completed_at}" 42 | end 43 | exit 44 | end 45 | 46 | fp = $stdin.read.strip if fp == "-" 47 | job = nil if job.downcase == "all" 48 | 49 | if not fp 50 | usage() 51 | end 52 | 53 | 54 | begin 55 | res = nil 56 | job = DialJob.find(job.to_i) if job 57 | if job 58 | res = DialResult.find_by_sql "SELECT dial_results.*, " + 59 | " (( icount('#{fp}'::int[] & dial_results.fprint::int[]) / icount('#{fp}'::int[])::float ) * 100.0 ) AS matchscore " + 60 | "FROM dial_results " + 61 | "WHERE " + 62 | " icount(dial_results.fprint) > 0 AND " + 63 | " dial_results.dial_job_id = '#{job.id}' " + 64 | "ORDER BY matchscore DESC" 65 | else 66 | res = DialResult.find_by_sql "SELECT dial_results.*, " + 67 | " (( icount('#{fp}'::int[] & dial_results.fprint::int[]) / icount('#{fp}'::int[])::float ) * 100.0 ) AS matchscore " + 68 | "FROM dial_results " + 69 | "WHERE " + 70 | " icount(dial_results.fprint) > 0 " + 71 | "ORDER BY matchscore DESC" 72 | end 73 | res.each do |r| 74 | $stdout.puts "#{"%.2f" % r.matchscore}\t#{r.dial_job_id}\t#{r.number}" 75 | end 76 | rescue ActiveRecord::RecordNotFound 77 | $stderr.puts "Job not found" 78 | exit 79 | end 80 | -------------------------------------------------------------------------------- /bin/import_audio.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'fileutils' 15 | 16 | 17 | ENV['RAILS_ENV'] ||= 'production' 18 | 19 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 20 | 21 | def usage 22 | $stderr.puts "Usage: #{$0} [Input Directory] " 23 | exit(1) 24 | end 25 | 26 | # 27 | # Script 28 | # 29 | 30 | dir = ARGV.shift() || usage() 31 | if (dir and dir =="-h") or (! dir) 32 | usage() 33 | end 34 | 35 | require 'config/boot' 36 | require 'config/environment' 37 | 38 | project_id = ARGV.shift 39 | provider_id = ARGV.shift 40 | 41 | todo = Dir["#{dir}/**/*.{raw,wav}"].to_a 42 | 43 | if todo.empty? 44 | $stderr.puts "Error: No raw audio files found within #{dir}" 45 | exit(1) 46 | end 47 | 48 | project = nil 49 | provider = nil 50 | 51 | if project_id 52 | project = Project.where(id: project_id).first 53 | unless project 54 | $stderr.puts "Error: Specified Project ID not found" 55 | exit(1) 56 | end 57 | end 58 | 59 | if provider_id 60 | provider = Provider.where(id: provider_id).first 61 | unless provider 62 | $stderr.puts "Error: Specified Provider ID not found" 63 | exit(1) 64 | end 65 | end 66 | 67 | unless project 68 | project = Project.create( 69 | name: "Import from #{dir} at #{Time.now.utc.to_s}", 70 | created_by: "importer" 71 | ) 72 | end 73 | 74 | provider = Provider.first 75 | unless provider 76 | provider = Provider.create( 77 | name: 'Import Provider', 78 | host: 'localhost', 79 | port: 4369, 80 | user: "null", 81 | pass: "null", 82 | lines: 1, 83 | enabled: false 84 | ) 85 | end 86 | 87 | 88 | job = Job.new 89 | job.project_id = project.id 90 | job.locked_by = "importer" 91 | job.locked_at = Time.now.utc 92 | job.started_at = Time.now.utc 93 | job.created_by = "importer" 94 | job.task = "import" 95 | job.args = Marshal.dump({ directory: dir, project_id: project.id, provider_id: provider.id }) 96 | job.status = "running" 97 | job.save! 98 | 99 | pct = 0 100 | cnt = 0 101 | 102 | todo.each do |rfile| 103 | num, ext = File.basename(rfile).split(".", 2) 104 | dr = Call.new 105 | dr.number = num 106 | dr.job_id = job.id 107 | dr.project_id = project.id 108 | dr.provider_id = provider.id 109 | dr.answered = true 110 | dr.busy = false 111 | dr.audio_length = File.size(rfile) / 16000.0 112 | dr.ring_length = 0 113 | dr.caller_id = num 114 | dr.save 115 | 116 | mr = dr.media 117 | ::File.open(rfile, "rb") do |fd| 118 | mr.audio = fd.read(fd.stat.size) 119 | mr.save 120 | end 121 | 122 | cnt += 1 123 | pct = (cnt / todo.length.to_f) * 100.0 124 | if cnt % 10 == 0 125 | job.update_progress(pct) 126 | end 127 | 128 | $stdout.puts "[ %#{"%.3d" % pct.to_i} ] Imported #{num} into project '#{project.name}' ##{project.id}" 129 | end 130 | 131 | job.update_progress(100) 132 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/resetpw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV['RAILS_ENV'] ||= 'production' 4 | 5 | # bundler/setup just sets up the $LOAD_PATHs, the gems aren't automatically required... 6 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile',File.dirname(__FILE__)) 7 | require 'bundler/setup' 8 | 9 | # rails/all must be required explicitly to get the railties that pro/ui/config/application.rb uses 10 | require 'rails/all' 11 | # require all the gems in the current environment 12 | Bundler.require(*Rails.groups(assets: %w(development test cucumber))) 13 | 14 | APP_PATH = File.expand_path('../../config/application', __FILE__) 15 | require File.expand_path('../../config/boot', __FILE__) 16 | require APP_PATH 17 | Rails.application.require_environment! 18 | 19 | uname = ARGV.shift 20 | upass = ARGV.shift 21 | 22 | def generate_password 23 | set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*") 24 | set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 25 | str = '' 26 | cnt = 0 27 | 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]/) 28 | if str.length > 12 29 | str = str[0,4] 30 | next 31 | end 32 | str << set[ rand(set.length), 1] 33 | cnt += 1 34 | end 35 | str 36 | end 37 | 38 | 39 | user = uname ? User.find_by_login(uname) : User.first 40 | if uname and not user 41 | $stderr.puts "[-] User #{uname} was not found" 42 | exit(1) 43 | end 44 | 45 | if not user 46 | $stderr.puts "[-] No user account has been created" 47 | exit(1) 48 | end 49 | 50 | randpass = upass || generate_password() 51 | 52 | 53 | $stdout.puts %Q| 54 | 55 | ******************************** 56 | * * 57 | * WarVOX Password Reset * 58 | * * 59 | ******************************** 60 | 61 | [*] Warning! This tool will reset the password for the '#{user.login}' user account. 62 | [*] To continue, please type "yes" 63 | 64 | | 65 | 66 | $stdout.write "Continue? (yes/no) > " 67 | $stdout.flush 68 | 69 | inp = $stdin.readline 70 | 71 | if inp.strip.downcase != 'yes' 72 | $stdout.puts "[*] Reset cancelled, hit enter to exit" 73 | $stdin.readline 74 | exit(0) 75 | end 76 | 77 | 78 | user.password = randpass 79 | user.password_confirmation = randpass 80 | user.save! 81 | 82 | $stdout.puts %Q| 83 | [*] The password for #{user.login} has been reset to a random value 84 | 85 | New Password: #{randpass} 86 | 87 | [*] Please change this password on the next login. 88 | | 89 | 90 | $stdout.puts "[*] Hit enter to exit" 91 | $stdin.readline 92 | exit(0) 93 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /bin/verify_install.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | require 'warvox' 13 | 14 | # 15 | # Verify that WarVOX has been installed properly 16 | # 17 | 18 | puts("**********************************************************************") 19 | puts("* *") 20 | puts("* WarVOX Installation Verifier *") 21 | puts("* *") 22 | puts("**********************************************************************") 23 | puts(" ") 24 | 25 | 26 | begin 27 | require 'rubygems' 28 | puts "[*] RubyGems have been installed" 29 | rescue ::LoadError 30 | puts "[*] ERROR: The RubyGems package has not been installed:" 31 | puts " $ sudo apt-get install rubygems" 32 | exit 33 | end 34 | 35 | begin 36 | require 'bundler' 37 | puts "[*] The Bundler gem has been installed" 38 | rescue ::LoadError 39 | puts "[*] ERROR: The Bundler gem has not been installed:" 40 | puts " $ sudo gem install bundler" 41 | exit 42 | end 43 | 44 | if(not WarVOX::Config.tool_path('gnuplot')) 45 | puts "[*] ERROR: The 'gnuplot' binary could not be installed" 46 | puts "[*] $ sudo apt-get install gnuplot" 47 | exit 48 | end 49 | puts "[*] The GNUPlot binary appears to be available" 50 | 51 | if(not WarVOX::Config.tool_path('lame')) 52 | puts "[*] ERROR: The 'lame' binary could not be installed" 53 | puts "[*] $ sudo apt-get install lame" 54 | exit 55 | end 56 | puts "[*] The LAME binary appears to be available" 57 | 58 | 59 | if(not WarVOX::Config.tool_path('sox')) 60 | puts "[*] ERROR: The 'sox binary could not be installed" 61 | puts "[*] $ sudo apt-get install sox" 62 | exit 63 | end 64 | puts "[*] The SOX binary appears to be available" 65 | 66 | 67 | puts " " 68 | puts "[*] Congratulations! You are almost ready to run WarVOX" 69 | puts " " 70 | puts "[*] Configuring the PostgreSQL database server:" 71 | puts "[*] 1. Install postgresql (9.1 or newer):" 72 | puts "[*] $ sudo apt-get install postgresql" 73 | puts "[*]" 74 | puts "[*] 2. Install postgresql community contributed modules:" 75 | puts "[*] $ sudo apt-get install postgresql-contrib" 76 | puts "[*]" 77 | puts "[*] 3. Configure a superuser account, password, and database for WarVOX:" 78 | puts "[*] $ sudo su - postgres" 79 | puts "[*] $ createuser -s warvox" 80 | puts "[*] $ createdb warvox -O warvox" 81 | puts "[*] $ psql" 82 | puts "[*] psql> alter user warvox with password 'randompass';" 83 | puts "[*] psql> exit" 84 | puts "[*]" 85 | puts "[*] 4. Copy config/database.yml.example to config/database.yml" 86 | puts "[*]" 87 | puts "[*] 5. Modify config/database.yml to include the password above" 88 | puts "[*]" 89 | puts "[*] 6. Initialize the WarVOX database" 90 | puts "[*] $ make database" 91 | puts "[*]" 92 | puts "[*] 7. Create a user account" 93 | puts "[*] $ bin/adduser" 94 | puts "[*]" 95 | puts "[*] 8. Start WarVOX with bin/warvox.rb" 96 | puts "[*]" 97 | puts "[*] 9. Login to http://127.0.0.1:7777/" 98 | puts "[*]" 99 | -------------------------------------------------------------------------------- /bin/warvox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | require 'getoptlong' 5 | require 'open3' 6 | 7 | # 8 | # Load the library path 9 | # 10 | base = __FILE__ 11 | while File.symlink?(base) 12 | base = File.expand_path(File.readlink(base), File.dirname(base)) 13 | end 14 | 15 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 16 | 17 | voxroot = File.expand_path(File.join(File.dirname(base), '..')) 18 | manager = File.expand_path(File.join(File.dirname(base), 'worker_manager.rb')) 19 | 20 | require 'warvox' 21 | 22 | 23 | Dir.chdir(voxroot) 24 | 25 | def stop 26 | $stderr.puts "[-] Interrupt received, shutting down workers and web server..." 27 | Process.kill("TERM", @manager_pid) if @manager_pid 28 | exit(0) 29 | end 30 | 31 | def usage 32 | $stderr.puts "#{$0} [--address IP] [--port PORT] --background" 33 | exit(0) 34 | end 35 | 36 | opts = 37 | { 38 | 'ServerPort' => 7777, 39 | 'ServerHost' => '127.0.0.1', 40 | 'Background' => false, 41 | } 42 | 43 | args = GetoptLong.new( 44 | ["--address", "-a", GetoptLong::REQUIRED_ARGUMENT ], 45 | ["--port", "-p", GetoptLong::REQUIRED_ARGUMENT ], 46 | ["--daemon", "-d", GetoptLong::NO_ARGUMENT ], 47 | ["--help", "-h", GetoptLong::NO_ARGUMENT] 48 | ) 49 | 50 | args.each do |opt,arg| 51 | case opt 52 | when '--address' 53 | opts['ServerHost'] = arg 54 | when '--port' 55 | opts['ServerPort'] = arg 56 | when '--daemon' 57 | opts['Background'] = true 58 | when '--help' 59 | usage() 60 | end 61 | end 62 | 63 | args = [ 64 | 'server', 65 | '-p', opts['ServerPort'].to_s, 66 | '-b', opts['ServerHost'], 67 | '-e', 'production', 68 | ] 69 | 70 | if opts['Background'] 71 | args.push("-d") 72 | end 73 | 74 | 75 | trap("SIGINT") { Thread.new{ stop } } 76 | 77 | $browser_url = "http://#{opts['ServerHost']}:#{opts['ServerPort']}/" 78 | 79 | WarVOX::Log.info("") 80 | WarVOX::Log.info("[*] Starting WarVOX on #{$browser_url}") 81 | WarVOX::Log.info("") 82 | WarVOX::Log.info("WarVOX is starting up...") 83 | 84 | @manager_pid = Process.fork() 85 | if not @manager_pid 86 | while ARGV.shift do 87 | end 88 | load(manager) 89 | exit(0) 90 | end 91 | 92 | WarVOX::Log.info("Worker Manager has PID #{@manager_pid}") 93 | 94 | @webserver_pid = $$ 95 | 96 | WarVOX::Log.info("Web Server has PID #{@webserver_pid}") 97 | 98 | while(ARGV.length > 0); ARGV.shift; end 99 | args.each {|arg| ARGV.push(arg) } 100 | 101 | # need to pass config opts above into the system command below 102 | system "bin/rails", *args 103 | -------------------------------------------------------------------------------- /bin/worker.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ################### 3 | 4 | # 5 | # Load the library path 6 | # 7 | base = __FILE__ 8 | while File.symlink?(base) 9 | base = File.expand_path(File.readlink(base), File.dirname(base)) 10 | end 11 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) 12 | 13 | require 'warvox' 14 | require 'fileutils' 15 | 16 | 17 | ENV['RAILS_ENV'] ||= 'production' 18 | $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) 19 | 20 | @task = nil 21 | @job = nil 22 | 23 | def usage 24 | $stderr.puts "Usage: #{$0} [JID]" 25 | exit(1) 26 | end 27 | 28 | def stop 29 | if @task 30 | @task.stop() rescue nil 31 | end 32 | if @job 33 | Job.where(id: @job_id).update_all({ status: 'stopped', completed_at: Time.now }) 34 | end 35 | exit(0) 36 | end 37 | 38 | # 39 | # Script 40 | # 41 | 42 | jid = ARGV.shift() || usage() 43 | if (jid and jid =="-h") or (! jid) 44 | usage() 45 | end 46 | 47 | require 'config/boot' 48 | require 'config/environment' 49 | 50 | trap("SIGTERM") { stop() } 51 | 52 | jid = jid.to_i 53 | 54 | @job = Job.where(id: jid).first 55 | 56 | unless @job 57 | $stderr.puts "Error: Specified job not found" 58 | WarVOX::Log.warn("Worker rejected invalid Job #{jid}") 59 | exit(1) 60 | end 61 | 62 | $0 = "warvox worker: #{jid} " 63 | 64 | Job.where(id: @job.id).update_all({ started_at: Time.now.utc, status: 'running'}) 65 | 66 | args = Marshal.load(@job.args) rescue {} 67 | 68 | 69 | WarVOX::Log.debug("Worker #{@job.id} #{@job.task} is running #{@job.task} with parameters #{ args.inspect }") 70 | 71 | begin 72 | 73 | case @job.task 74 | when 'dialer' 75 | @task = WarVOX::Jobs::Dialer.new(@job.id, args) 76 | @task.start 77 | when 'analysis' 78 | @task = WarVOX::Jobs::Analysis.new(@job.id, args) 79 | @task.start 80 | else 81 | Job.where(id: @job.id).update_all({ error: 'unsupported', status: 'error' }) 82 | end 83 | 84 | @job.update_progress(100) 85 | 86 | rescue ::SignalException, ::SystemExit 87 | raise $! 88 | rescue ::Exception => e 89 | WarVOX::Log.warn("Worker #{@job.id} #{@job.task} threw an exception: #{e.class} #{e} #{e.backtrace}") 90 | Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}", status: 'error', completed_at: Time.now.utc }) 91 | end 92 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Web::Application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_record/railtie" 7 | require "action_controller/railtie" 8 | require "action_mailer/railtie" 9 | require "action_view/railtie" 10 | require "sprockets/railtie" 11 | 12 | # Require the gems listed in Gemfile, including any gems 13 | # you've limited to :test, :development, or :production. 14 | Bundler.require(*Rails.groups) 15 | 16 | module Web 17 | class Application < Rails::Application 18 | # Settings in config/environments/* take precedence over those specified here. 19 | # Application configuration should go into files in config/initializers 20 | # -- all .rb files in that directory are automatically loaded. 21 | 22 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 23 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 24 | # config.time_zone = 'Central Time (US & Canada)' 25 | 26 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 27 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 28 | # config.i18n.default_locale = :de 29 | 30 | # Bootstrap the WarVOX code base 31 | config.autoload_paths << "#{Rails.root}/lib" 32 | require 'warvox' 33 | 34 | config.encoding = "utf-8" 35 | 36 | # Enable escaping HTML in JSON. 37 | config.active_support.escape_html_entities_in_json = true 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /config/blacklist.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Fill this with a list of regular expressions to match all 3 | # dialed numbers against. No number matching an entry in this 4 | # file should ever be dialed. 5 | # 6 | 7 | # Never dial just zero 8 | ^0$ 9 | 10 | # Skip all of the [X]11 numbers, such as 911, 411, and 111 11 | ^[0-9]11$ 12 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /config/classifiers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/config/classifiers/.keep -------------------------------------------------------------------------------- /config/classifiers/01.default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # WarVOX Classifiers 3 | # 4 | 5 | # 6 | # These lightweight signatures are used to determine the 7 | # line type, which is the top-level classification that 8 | # differentiates between modem, fax, voice, and other 9 | # common results. 10 | # 11 | 12 | 13 | # 14 | # If you want to force your checks to run first, add your 15 | # logic to a file starting with "00." and place it in 16 | # this directory. Signature files are processed numerically 17 | # from lowest to highest (like RC scripts) 18 | # 19 | 20 | 21 | # 22 | # Initialize some local variables out of data 23 | # 24 | freq = data[:freq] 25 | fcnt = data[:fcnt] 26 | maxf = data[:maxf] 27 | 28 | # 29 | # Look for modems by detecting a 2100hz answer + 2250hz tone 30 | # 31 | if( (fcnt[2100] > 1.0 or fcnt[2230] > 1.0) and fcnt[2250] > 0.5) 32 | @line_type = 'modem' 33 | raise Completed 34 | end 35 | 36 | # 37 | # Look for modems by detecting a peak frequency of 2250hz 38 | # 39 | if(fcnt[2100] > 1.0 and (maxf > 2245.0 and maxf < 2255.0)) 40 | @line_type = 'modem' 41 | raise Completed 42 | end 43 | 44 | # 45 | # Look for modems by detecting a peak frequency of 3000hz 46 | # 47 | if(fcnt[2100] > 1.0 and (maxf > 2995.0 and maxf < 3005.0)) 48 | @line_type = 'modem' 49 | raise Completed 50 | end 51 | 52 | # 53 | # Look for faxes by checking for a handful of tones (min two) 54 | # 55 | fax_sum = 0 56 | [ 57 | fcnt[1625], fcnt[1660], fcnt[1825], fcnt[2100], 58 | fcnt[600], fcnt[1855], fcnt[1100], fcnt[2250], 59 | fcnt[2230], fcnt[2220], fcnt[1800], fcnt[2095], 60 | fcnt[2105] 61 | ].map{|x| fax_sum += [x,1.0].min } 62 | if(fax_sum >= 2.0) 63 | @line_type = 'fax' 64 | raise Completed 65 | end 66 | 67 | # 68 | # Dial tone detection (440hz + 350hz) 69 | # 70 | if(fcnt[440] > 1.0 and fcnt[350] > 1.0) 71 | @line_type = 'dialtone' 72 | raise Completed 73 | end 74 | 75 | # 76 | # To use additional heuristics, add new scripts to this directory 77 | # named XX.myscript.rb, where XX is a two digit number less than 78 | # 99 and greater than 01. 79 | # 80 | # 81 | -------------------------------------------------------------------------------- /config/classifiers/99.default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # WarVOX Classifiers 3 | # 4 | # 5 | 6 | # 7 | # Initialize some local variables out of data 8 | # 9 | freq = data[:freq] 10 | fcnt = data[:fcnt] 11 | maxf = data[:maxf] 12 | 13 | # Look for voice mail by detecting the 1000hz BEEP 14 | # If the call length was too short to catch the beep, 15 | # this signature can fail. For non-US numbers, the beep 16 | # is often a different frequency entirely. 17 | if(fcnt[1000] >= 1.0) 18 | @line_type = 'voicemail' 19 | raise Completed 20 | end 21 | 22 | # Look for voicemail by detecting a peak frequency of 23 | # 1000hz. Not as accurate, but thats why this is in 24 | # the fallback script. 25 | if(maxf > 995 and maxf < 1005) 26 | @line_type = 'voicemail' 27 | raise Completed 28 | end 29 | 30 | 31 | # 32 | # Fall back to 'voice' if nothing else has been matched 33 | # This should be the last check processed 34 | # 35 | 36 | @line_type = 'voice' 37 | -------------------------------------------------------------------------------- /config/database.yml.example: -------------------------------------------------------------------------------- 1 | production: 2 | adapter: postgresql 3 | database: warvox 4 | username: warvox 5 | password: "" 6 | host: 127.0.0.1 7 | port: 5432 8 | pool: 100 9 | timeout: 5 10 | 11 | development: 12 | adapter: postgresql 13 | database: warvox 14 | username: warvox 15 | password: "" 16 | host: 127.0.0.1 17 | port: 5432 18 | pool: 100 19 | timeout: 5 20 | 21 | 22 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | config.log_level = :debug 41 | 42 | # Debug mode disables concatenation and preprocessing of assets. 43 | # This option may cause significant delays in view rendering with a large 44 | # number of complex assets. 45 | config.assets.debug = false 46 | 47 | # Suppress logger output for asset requests. 48 | config.assets.quiet = true 49 | 50 | # Raises error for missing translations 51 | # config.action_view.raise_on_missing_translations = true 52 | 53 | # Use an evented file watcher to asynchronously detect changes in source code, 54 | # routes, locales, etc. This feature depends on the listen gem. 55 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 56 | end 57 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | # Compress JavaScripts and CSS. 22 | config.assets.js_compressor = :uglifier 23 | # config.assets.css_compressor = :sass 24 | 25 | # Do not fallback to assets pipeline if a precompiled asset is missed. 26 | config.assets.compile = false 27 | 28 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 29 | 30 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 31 | # config.action_controller.asset_host = 'http://assets.example.com' 32 | 33 | # Specifies the header that your server uses for sending files. 34 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 35 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 36 | 37 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 38 | # config.force_ssl = true 39 | 40 | # Use the lowest log level to ensure availability of diagnostic information 41 | # when problems arise. 42 | config.log_level = :debug 43 | 44 | # Prepend all log lines with the following tags. 45 | config.log_tags = [ :request_id ] 46 | 47 | # Use a different cache store in production. 48 | # config.cache_store = :mem_cache_store 49 | 50 | # Use a real queuing backend for Active Job (and separate queues per environment) 51 | # config.active_job.queue_adapter = :resque 52 | # config.active_job.queue_name_prefix = "barerails5app_#{Rails.env}" 53 | config.action_mailer.perform_caching = false 54 | 55 | # Ignore bad email addresses and do not raise email delivery errors. 56 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 57 | # config.action_mailer.raise_delivery_errors = false 58 | 59 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 60 | # the I18n.default_locale when a translation cannot be found). 61 | config.i18n.fallbacks = true 62 | 63 | # Send deprecation notices to registered listeners. 64 | config.active_support.deprecation = :notify 65 | 66 | # Use default logging formatter so that PID and timestamp are not suppressed. 67 | config.log_formatter = ::Logger::Formatter.new 68 | 69 | # Use a different logger for distributed setups. 70 | # require 'syslog/logger' 71 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 72 | 73 | if ENV["RAILS_LOG_TO_STDOUT"].present? 74 | logger = ActiveSupport::Logger.new(STDOUT) 75 | logger.formatter = config.log_formatter 76 | config.logger = ActiveSupport::TaggedLogging.new(logger) 77 | end 78 | 79 | # Do not dump schema after migrations. 80 | config.active_record.dump_schema_after_migration = false 81 | end 82 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | 13 | # Rails.application.config.assets.precompile += %w( jquery.js ) 14 | 15 | Rails.application.config.assets.precompile += %w( html5.js ) 16 | Rails.application.config.assets.precompile += %w(analyze/_index.coffee analyze/view.coffee jobs/view_results.coffee) 17 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :marshal 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password, :pass, :password, :password_confirmation] 5 | -------------------------------------------------------------------------------- /config/initializers/formtastic.rb: -------------------------------------------------------------------------------- 1 | Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder 2 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Rails 5.0 release notes for more info on each option. 6 | 7 | # Enable per-form CSRF tokens. Previous versions had false. 8 | Rails.application.config.action_controller.per_form_csrf_tokens = true 9 | 10 | # Enable origin-checking CSRF mitigation. Previous versions had false. 11 | Rails.application.config.action_controller.forgery_protection_origin_check = true 12 | 13 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 14 | # Previous versions had false. 15 | ActiveSupport.to_time_preserves_timezone = true 16 | 17 | # Require `belongs_to` associations by default. Previous versions had false. 18 | Rails.application.config.active_record.belongs_to_required_by_default = true 19 | 20 | # Do not halt callback chains when a callback returns false. Previous versions had true. 21 | ActiveSupport.halt_callback_chains_on_return_false = false 22 | 23 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 24 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 25 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_warvox_session' 4 | -------------------------------------------------------------------------------- /config/initializers/warvox.rb: -------------------------------------------------------------------------------- 1 | 2 | # Extend PostgreSQL 3 | # require 'postgres_ext' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.bootstrap.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | breadcrumbs: 6 | application: 7 | root: "Index" 8 | pages: 9 | pages: "Pages" 10 | helpers: 11 | actions: "Actions" 12 | links: 13 | back: "Back" 14 | cancel: "Cancel" 15 | confirm: "Are you sure?" 16 | destroy: "Delete" 17 | new: "New" 18 | edit: "Edit" 19 | titles: 20 | edit: "Edit %{model}" 21 | save: "Save %{model}" 22 | new: "New %{model}" 23 | delete: "Delete %{model}" 24 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get "login" => "user_sessions#new", :as => "login" 3 | get "logout" => "user_sessions#destroy", :as => "logout" 4 | 5 | resources :user_sessions 6 | 7 | get '/projects/:project_id/all' => 'projects#index', :as => :all_projects 8 | 9 | 10 | get '/jobs/dial' => 'jobs#new_dialer', :as => :new_dialer_job 11 | get '/projects/:project_id/jobs/dial' => 'jobs#new_dialer', :as => :new_dialer_project_job 12 | put '/jobs/dialer' => 'jobs#dialer', :as => :dialer_job 13 | 14 | get '/jobs/analyze' => 'jobs#new_analyze', :as => :new_analyze_job 15 | get '/projects/:project_id/jobs/analyze' => 'jobs#new_analyze', :as => :new_analyze_project_job 16 | put '/jobs/analyzer' => 'jobs#analyzer', :as => :analyzer_job 17 | 18 | get '/projects/:project_id/jobs/identify' => 'jobs#new_identify', :as => :new_identify_project_job 19 | put '/jobs/identifier' => 'jobs#identifier', :as => :identifier_job 20 | 21 | get '/jobs/:id/stop' => 'jobs#stop', :as => :stop_job 22 | post '/jobs/:id/calls/purge' => "jobs#purge_calls", :as => :purge_calls_job 23 | 24 | post '/projects/:project_id/calls/purge' => "jobs#purge_calls", :as => :purge_calls_project_job 25 | 26 | get '/projects/:project_id/scans' => 'jobs#results', :as => :results 27 | get '/projects/:project_id/scans/:id' => 'jobs#view_results', :as => :view_results 28 | get '/projects/:project_id/scans/:id/analyze' => 'jobs#analyze_job', :as => :analyze_job 29 | get '/projects/:project_id/scans/:id/reanalyze' => 'jobs#reanalyze_job', :as => :reanalyze_job 30 | 31 | put '/projects/:project_id/calls/analyze' => 'jobs#analyze_project', :as => :analyze_project_job 32 | put '/projects/:project_id/calls/identify' => 'jobs#identify_project', :as => :identify_project_job 33 | 34 | 35 | get '/projects/:project_id/analyze' => 'analyze#index', :as => :analyze 36 | get '/call/:result_id/:rtype' => 'analyze#resource', :as => :resource_analyze 37 | get '/projects/:project_id/analyze/:id/view' => 'analyze#view', :as => :view_analyze 38 | 39 | get '/projects/:project_id/analyze/:job_id/:call_id/matches' => 'analyze#view_matches', :as => :view_matches 40 | get '/projects/:project_id/analyze/:call_id/matches' => 'analyze#view_matches', :as => :view_matches_project 41 | 42 | resources :settings 43 | resources :providers 44 | resources :users 45 | resources :projects 46 | resources :jobs 47 | resources :calls 48 | 49 | get '/about' => 'home#about', :as => :about 50 | get '/help' => 'home#help', :as => :help 51 | get '/check' => 'home#check', :as => :check 52 | 53 | root to: "projects#index" 54 | end 55 | -------------------------------------------------------------------------------- /config/secrets.yml.example: -------------------------------------------------------------------------------- 1 | development: 2 | secret_key_base: <%= WarVOX::Config.load_session_key %> 3 | 4 | test: 5 | secret_key_base: <%= WarVOX::Config.load_session_key %> 6 | 7 | production: 8 | secret_key_base: <%= WarVOX::Config.load_session_key % 9 | -------------------------------------------------------------------------------- /config/signatures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/config/signatures/.keep -------------------------------------------------------------------------------- /config/unicorn.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/config/unicorn.rb -------------------------------------------------------------------------------- /config/warvox.conf: -------------------------------------------------------------------------------- 1 | # 2 | # WarVOX Configuration 3 | # 4 | 5 | # 6 | # Configure filesystem paths to each required tool 7 | # 8 | tools: 9 | gnuplot: gnuplot 10 | lame: lame 11 | sox: sox 12 | iaxrecord: "%BASE%/bin/iaxrecord.rb" 13 | 14 | 15 | # 16 | # Maximum processing jobs, normally this is set to your processor core count, 17 | # but you can limit it further here. Keep in mind that each analysis job also 18 | # requires at least 512Mb of RAM (based on audio capture length). A 300 second 19 | # audio capture could consume 3Gb of RAM per thread (the default is 53 seconds). 20 | # 21 | max_analysis_threads: 0 22 | 23 | # 24 | # Configure the dial blacklist location 25 | # 26 | blacklist: "%BASE%/config/blacklist.txt" 27 | 28 | # 29 | # Configure the classifier directory 30 | # 31 | classifiers: "%BASE%/config/classifiers" 32 | 33 | # 34 | # Configure the signature directory 35 | # 36 | signatures: "%BASE%/config/signatures" 37 | 38 | # 39 | # Configure cloud integrations 40 | # 41 | apis: 42 | gcloud: "%BASE%/config/gcloud.key" 43 | -------------------------------------------------------------------------------- /db/migrate/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/db/migrate/.keep -------------------------------------------------------------------------------- /db/migrate/20130113004653_create_reportable_cache.rb: -------------------------------------------------------------------------------- 1 | class CreateReportableCache < ActiveRecord::Migration[5.0] 2 | def up 3 | create_table :reportable_cache, force: true do |t| 4 | t.string :model_name, null: false, limit: 100 5 | t.string :report_name, null: false, limit: 100 6 | t.string :grouping, null: false, limit: 10 7 | t.string :aggregation, null: false, limit: 10 8 | t.string :conditions, null: false, limit: 100 9 | t.float :value, null: false, default: 0 10 | t.datetime :reporting_period, null: false 11 | 12 | t.timestamps null: false 13 | end 14 | 15 | add_index :reportable_cache, [ 16 | :model_name, 17 | :report_name, 18 | :grouping, 19 | :aggregation, 20 | :conditions 21 | ], name: :name_model_grouping_agregation 22 | add_index :reportable_cache, [ 23 | :model_name, 24 | :report_name, 25 | :grouping, 26 | :aggregation, 27 | :conditions, 28 | :reporting_period 29 | ], unique: true, name: :name_model_grouping_aggregation_period 30 | end 31 | 32 | def self.down 33 | remove_index :reportable_cache, name: :name_model_grouping_agregation 34 | remove_index :reportable_cache, name: :name_model_grouping_aggregation_period 35 | 36 | drop_table :reportable_cache 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /docs/BUGS: -------------------------------------------------------------------------------- 1 | KNOWN BUGS 2 | 3 | * Stopping WarVOX in mid-scan will leave a hung scan job, the job will 4 | need to be removed and restarted. 5 | -------------------------------------------------------------------------------- /docs/ChangeLog: -------------------------------------------------------------------------------- 1 | 2012-12-26 HD Moore 2 | * overhauled, updated gems, merged directories with web 3 | * swapped in Rapid7-licensed Highcharts for graphics 4 | * remove dtmf2num (limited value, too inaccurate) 5 | 6 | 2011-02-23 HD Moore 7 | * bumped the version number to 1.2.0 8 | * applied patch from mediaservice for iaxclient v2 test mode 9 | * fixed pagination issues with rails 3 10 | * updated the copyright for 2011 11 | 12 | 2010-11-03 HD Moore 13 | * bumped the version number to 1.1.0 14 | * upgraded the web interface to Rails 3 15 | * added support for Ruby 1.9.1 16 | * updated README and LICENSE 17 | 18 | 2009-05-25 HD Moore 19 | * switched MP3 quality to 32kbps from 8kpbs for better listening 20 | * added bin/warvox.agi as an asterisk plugin to allow job re-dialing 21 | * overhaul of the signature system (etc/sigs/*.rb) 22 | * filter analysis results by line type 23 | * overhaul of results view + show signatures 24 | * added dtmf2num support to decode dtmf tones 25 | 26 | 2009-05-10 HD Moore 27 | * release of version 1.0.1 28 | * switched to the BSD license for WarVOX 29 | * swapped the sox -w flag to -2 (deprecated) 30 | * added support for multiple dial masks for a single job 31 | * added support for number exclusions (blacklists) 32 | * added support for file input for a dial range 33 | * fixed a hang with pulse audio on some systems 34 | * added getopt parser to iaxrecord.c 35 | 36 | 2009-03-05 HD Moore 37 | * initial public release of version 1.0.0 38 | -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2014, Rapid7, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Rapid7, Inc. nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | ================================================================================ 30 | 31 | WarVOX is provided under the BSD license above. 32 | 33 | The copyright on this package is held by Rapid7, Inc. 34 | 35 | This copyright and license does not apply to the following components: 36 | - The ruby on rails vendor libraries under web/vendor/ (Ruby license) 37 | - The kissfft gem (BSD) 38 | - The Rex library gem (BSD) 39 | - The highcharts library is included under a commercial license agreement 40 | between Rapid7 and Highsoft Solutions AS. This license does not provide 41 | redistribution rights to third-parties. 42 | 43 | The latest version of this software is available from http://github.com/rapid7/warvox 44 | 45 | Questions and suggestions can be sent to research[at]rapid7.com 46 | -------------------------------------------------------------------------------- /docs/LICENSE.musicplayer: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005, Fabricio Zuardi 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/warvox.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # top level include file for warvox libaries 3 | ## 4 | 5 | # Load components 6 | require 'warvox/config' 7 | require 'warvox/jobs' 8 | require 'warvox/phone' 9 | require 'warvox/audio' 10 | require 'logger' 11 | 12 | # Global configuration 13 | module WarVOX 14 | VERSION = '2.0.0-dev' 15 | Base = File.expand_path(File.join(File.dirname(__FILE__), '..')) 16 | Conf = File.expand_path(File.join(Base, 'config', 'warvox.conf')) 17 | Log = Logger.new( WarVOX::Config.log_file ) 18 | Log.level = WarVOX::Config.log_level 19 | 20 | end 21 | -------------------------------------------------------------------------------- /lib/warvox/audio.rb: -------------------------------------------------------------------------------- 1 | require 'warvox/audio/error' 2 | require 'warvox/audio/raw' 3 | -------------------------------------------------------------------------------- /lib/warvox/audio/error.rb: -------------------------------------------------------------------------------- 1 | module WarVOX 2 | module Audio 3 | class Error < ::RuntimeError 4 | 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/warvox/jobs.rb: -------------------------------------------------------------------------------- 1 | module WarVOX 2 | class JobQueue 3 | attr_accessor :active_job, :active_thread, :queue, :queue_thread 4 | 5 | require "thread" 6 | 7 | def initialize 8 | @mutex = ::Mutex.new 9 | @queue = [] 10 | @queue_thread = Thread.new{ manage_queue } 11 | 12 | super 13 | end 14 | 15 | def scheduled?(klass, job_id) 16 | @mutex.synchronize do 17 | [@active_job, *(@queue)].each do |c| 18 | next if not c 19 | return true if (c.class == klass and c.name == job_id) 20 | end 21 | end 22 | false 23 | end 24 | 25 | def schedule(klass, job_id) 26 | begin 27 | return false if scheduled?(klass, job_id) 28 | @queue.push(klass.new(job_id)) 29 | rescue ::Exception => e 30 | $stderr.puts "ERROR!!!!!: #{e} #{e.backtrace}" 31 | false 32 | end 33 | end 34 | 35 | def stop(job_id) 36 | @mutex.synchronize do 37 | [@active_job, *(@queue)].each do |c| 38 | next if not c 39 | if c.name == job_id 40 | # Actively running 41 | if @active_job == c 42 | 43 | else 44 | 45 | end 46 | end 47 | end 48 | end 49 | end 50 | 51 | def manage_queue 52 | begin 53 | while(true) 54 | @mutex.synchronize do 55 | if(@active_job and @active_job.status == 'completed') 56 | @active_job = nil 57 | @active_thread = nil 58 | end 59 | 60 | if(not @active_job and @queue.length > 0) 61 | @active_job = @queue.shift 62 | @active_thread = Thread.new { @active_job.start } 63 | end 64 | end 65 | 66 | Kernel.select(nil, nil, nil, 1) 67 | end 68 | rescue ::Exception 69 | $stderr.puts "QUEUE MANAGER:#{$!.class} #{$!}" 70 | $stderr.flush 71 | end 72 | end 73 | 74 | end 75 | end 76 | 77 | 78 | require 'warvox/jobs/base' 79 | require 'warvox/jobs/dialer' 80 | require 'warvox/jobs/analysis' 81 | -------------------------------------------------------------------------------- /lib/warvox/jobs/base.rb: -------------------------------------------------------------------------------- 1 | module WarVOX 2 | module Jobs 3 | class Base 4 | attr_accessor :name, :status 5 | 6 | def type 7 | 'base' 8 | end 9 | 10 | def stop 11 | @status = 'active' 12 | end 13 | 14 | def start 15 | @status = 'completed' 16 | end 17 | 18 | def db_save(obj) 19 | max_tries = 100 20 | cur_tries = 0 21 | obj.save! 22 | end 23 | 24 | def clear_zombies 25 | self.class.clear_zombies 26 | end 27 | 28 | def self.clear_zombies 29 | begin 30 | # Clear zombies just in case... 31 | while(Process.waitpid(-1, Process::WNOHANG)) 32 | end 33 | rescue ::Exception 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/warvox/phone.rb: -------------------------------------------------------------------------------- 1 | module WarVOX 2 | class Phone 3 | 4 | # Convert 123456XXXX to an array of expanded numbers 5 | def self.crack_mask(mask) 6 | masks = mask.split(/[\s,]+/) || [ ] 7 | masks.delete(nil) 8 | self.crack_masks(masks) 9 | end 10 | 11 | def self.crack_masks(masks) 12 | res = {} 13 | masks.each do |mask| 14 | mask = mask.strip 15 | 16 | if(mask.index(':')) 17 | next if mask.index('X') 18 | 19 | # Quick hack to fix leading 0s 20 | prefix = "" 21 | if mask =~ /^(0+)/ 22 | prefix = $1 23 | end 24 | 25 | rbeg,rend = mask.split(':').map{|c| c.gsub(/[^\d]/, '').to_i } 26 | rbeg.upto(rend) do |n| 27 | res[prefix + n.to_s] = {} 28 | end 29 | next 30 | end 31 | 32 | incdigits = 0 33 | mask.each_char do |c| 34 | incdigits += 1 if c =~ /^[X#]$/i 35 | end 36 | 37 | max = (10**incdigits)-1 38 | 39 | (0..max).each do |num| 40 | number = mask.dup # copy the mask 41 | numstr = sprintf("%0#{incdigits}d", num) # stringify our incrementing number 42 | j = 0 # index for numstr 43 | for i in 0..number.length-1 do # step through the number (mask) 44 | if number[i].chr =~ /^[X#]$/i 45 | number[i] = numstr[j] # replaced masked indexes with digits from incrementing number 46 | j += 1 47 | end 48 | end 49 | res[number] = {} 50 | end 51 | 52 | end 53 | 54 | return res.keys.sort 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/warvox/proto/iax2.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'warvox/proto/iax2/client' 3 | -------------------------------------------------------------------------------- /lib/warvox/proto/iax2/codecs.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'warvox/proto/iax2/codecs/g711' 3 | require 'warvox/proto/iax2/codecs/mulaw' 4 | require 'warvox/proto/iax2/codecs/alaw' 5 | 6 | -------------------------------------------------------------------------------- /lib/warvox/proto/iax2/codecs/alaw.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module WarVOX 3 | module Proto 4 | module IAX2 5 | module Codecs 6 | class ALaw < G711 7 | 8 | def self.decode(buff) 9 | buff.unpack("C*").map{ |x| LOOKUP_ALAW2LIN16[x] }.pack('v*') 10 | end 11 | 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/warvox/proto/iax2/codecs/mulaw.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module WarVOX 3 | module Proto 4 | module IAX2 5 | module Codecs 6 | class MuLaw < G711 7 | 8 | def self.decode(buff) 9 | buff.unpack("C*").map{ |x| LOOKUP_ULAW2LIN16[x] }.pack('v*') 10 | end 11 | 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /spec/factories/call_media.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: call_media 4 | # 5 | # id :integer not null, primary key 6 | # call_id :integer not null 7 | # project_id :integer not null 8 | # audio :binary 9 | # mp3 :binary 10 | # png_big :binary 11 | # png_big_dots :binary 12 | # png_big_freq :binary 13 | # png_sig :binary 14 | # png_sig_freq :binary 15 | # 16 | 17 | FactoryGirl.define do 18 | factory :call_medium do 19 | call 20 | project 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /spec/factories/calls.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: calls 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # number :text not null 9 | # project_id :integer not null 10 | # job_id :integer not null 11 | # provider_id :integer not null 12 | # answered :boolean 13 | # busy :boolean 14 | # error :text 15 | # audio_length :integer 16 | # ring_length :integer 17 | # caller_id :text 18 | # analysis_job_id :integer 19 | # analysis_started_at :datetime 20 | # analysis_completed_at :datetime 21 | # peak_freq :float 22 | # peak_freq_data :text 23 | # line_type :text 24 | # fprint :integer is an Array 25 | # 26 | 27 | FactoryGirl.define do 28 | factory :call do 29 | project 30 | job 31 | provider 32 | number { Faker::PhoneNumber.phone_number } 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /spec/factories/jobs.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: jobs 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # project_id :integer not null 9 | # locked_by :string(255) 10 | # locked_at :datetime 11 | # started_at :datetime 12 | # completed_at :datetime 13 | # created_by :string(255) 14 | # task :string(255) not null 15 | # args :binary 16 | # status :string(255) 17 | # error :text 18 | # progress :integer default(0) 19 | # 20 | 21 | FactoryGirl.define do 22 | factory :job do 23 | project 24 | task 'dialer' 25 | args "\x04\b{\t:\nrangeI\"\x0F7632458942\x06:\x06ET:\nlinesi\x0F:\fsecondsi::\rcid_maskI\"\tSELF\x06;\x06T" 26 | status 'submitted' 27 | error nil 28 | range { Faker::PhoneNumber.phone_number } 29 | cid_mask { Faker::PhoneNumber.phone_number } 30 | seconds { Faker::Number.between(1, 299) } 31 | lines { Faker::Number.between(1, 10000) } 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /spec/factories/lines.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: lines 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # number :text not null 9 | # project_id :integer not null 10 | # line_type :text 11 | # notes :text 12 | # 13 | 14 | FactoryGirl.define do 15 | factory :line do 16 | project 17 | number { Faker::PhoneNumber.phone_number } 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /spec/factories/projects.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: projects 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # description :text 10 | # included :text 11 | # excluded :text 12 | # created_by :string(255) 13 | # 14 | 15 | FactoryGirl.define do 16 | factory :project do 17 | name { Faker::Lorem.sentence } 18 | description { Faker::Lorem.sentence } 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/factories/providers.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: providers 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # host :text not null 10 | # port :integer not null 11 | # user :text 12 | # pass :text 13 | # lines :integer default(1), not null 14 | # enabled :boolean default(TRUE) 15 | # 16 | 17 | FactoryGirl.define do 18 | factory :provider do 19 | name { Faker::Company.name } 20 | host { Faker::Internet.ip_v4_address } 21 | port { Faker::Number.between(1, 65535) } 22 | user { Faker::Internet.user_name } 23 | pass { Faker::Internet.password(10, 20) } 24 | lines { Faker::Number.between(1, 254) } 25 | enabled true 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /spec/factories/settings.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: settings 4 | # 5 | # id :integer not null, primary key 6 | # var :string(255) not null 7 | # value :text 8 | # thing_id :integer 9 | # thing_type :string(30) 10 | # created_at :datetime 11 | # updated_at :datetime 12 | # 13 | 14 | FactoryGirl.define do 15 | factory :setting, class: 'Settings' do 16 | var "CachedStuff" 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /spec/factories/signature_fps.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :signature_fp do 3 | 4 | end 5 | 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/signatures.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: signatures 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # source :string(255) 10 | # description :text 11 | # category :string(255) 12 | # line_type :string(255) 13 | # risk :integer 14 | # 15 | 16 | FactoryGirl.define do 17 | factory :signature do 18 | name { Faker::Commerce.product_name } 19 | source { Faker::PhoneNumber.cell_phone } 20 | description { Faker::Lorem.sentence } 21 | category { Faker::Lorem.word } 22 | line_type { Faker::Lorem.word } 23 | risk { Faker::Lorem.word } 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # login :string(255) not null 7 | # email :string(255) 8 | # crypted_password :string(255) not null 9 | # password_salt :string(255) not null 10 | # persistence_token :string(255) not null 11 | # single_access_token :string(255) not null 12 | # perishable_token :string(255) not null 13 | # login_count :integer default(0), not null 14 | # failed_login_count :integer default(0), not null 15 | # last_request_at :datetime 16 | # current_login_at :datetime 17 | # last_login_at :datetime 18 | # current_login_ip :string(255) 19 | # last_login_ip :string(255) 20 | # created_at :datetime 21 | # updated_at :datetime 22 | # enabled :boolean default(TRUE) 23 | # admin :boolean default(TRUE) 24 | # 25 | 26 | FactoryGirl.define do 27 | factory :user do 28 | login { Faker::Internet.user_name } 29 | password 'RandomPass' 30 | password_confirmation 'RandomPass' 31 | enabled true 32 | admin true 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /spec/features/projects_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.feature "Projects", type: :feature do 4 | 5 | before(:each) do 6 | @user = create(:user) 7 | create_user_session(@user) 8 | end 9 | 10 | it "list all existing projects" do 11 | project = create(:project) 12 | visit projects_path 13 | expect(page).to have_content "WarVOX Projects" 14 | within "#projects-table" do 15 | expect(page).to have_content "Name" 16 | expect(page).to have_content "Description" 17 | expect(page).to have_content "Jobs" 18 | expect(page).to have_content "Calls" 19 | expect(page).to have_content "Analyzed" 20 | expect(page).to have_content "Created" 21 | expect(page).to have_content "Actions" 22 | expect(page).to have_content project.name 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/features/visitor/logins_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.feature "Logins", type: :feature do 4 | it "login with valid credentials" do 5 | user = create(:user) 6 | visit login_path 7 | within "#new_user_session" do 8 | expect(page).to have_content "Username" 9 | expect(page).to have_content "Password" 10 | fill_in "user_session_login", with: user.login 11 | fill_in "user_session_password", with: 'RandomPass' 12 | click_button "Sign in" 13 | end 14 | within "div.content" do 15 | expect(page).to have_content "WarVOX Projects" 16 | end 17 | end 18 | 19 | it "failed login with invalid password valid username" do 20 | user = create(:user) 21 | visit login_path 22 | within "#new_user_session" do 23 | fill_in "user_session_login", with: user.login 24 | fill_in "user_session_password", with: 'WrongPassword' 25 | click_button "Sign in" 26 | end 27 | expect(page).to have_content "Password is not valid" 28 | end 29 | 30 | it "failed login with invalid username valid password" do 31 | user = create(:user) 32 | visit login_path 33 | within "#new_user_session" do 34 | fill_in "user_session_login", with: user.login + "Wrong" 35 | fill_in "user_session_password", with: 'RandomPass' 36 | click_button "Sign in" 37 | end 38 | expect(page).to have_content "Login is not valid" 39 | end 40 | 41 | it "failed login with no input entered" do 42 | visit login_path 43 | within "#new_user_session" do 44 | click_button "Sign in" 45 | end 46 | expect(page).to have_content "You did not provide any details for authentication." 47 | end 48 | 49 | it "failed login with no password entered" do 50 | user = create(:user) 51 | visit login_path 52 | within "#new_user_session" do 53 | fill_in "user_session_login", with: user.login 54 | click_button "Sign in" 55 | end 56 | expect(page).to have_content "Password cannot be blank" 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/models/call_medium_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: call_media 4 | # 5 | # id :integer not null, primary key 6 | # call_id :integer not null 7 | # project_id :integer not null 8 | # audio :binary 9 | # mp3 :binary 10 | # png_big :binary 11 | # png_big_dots :binary 12 | # png_big_freq :binary 13 | # png_sig :binary 14 | # png_sig_freq :binary 15 | # 16 | 17 | require 'rails_helper' 18 | 19 | RSpec.describe CallMedium, type: :model do 20 | it { should belong_to(:call) } 21 | it { should belong_to(:project) } 22 | 23 | it "valid record" do 24 | expect(build(:call_medium)).to be_valid 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/models/call_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: calls 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # number :text not null 9 | # project_id :integer not null 10 | # job_id :integer not null 11 | # provider_id :integer not null 12 | # answered :boolean 13 | # busy :boolean 14 | # error :text 15 | # audio_length :integer 16 | # ring_length :integer 17 | # caller_id :text 18 | # analysis_job_id :integer 19 | # analysis_started_at :datetime 20 | # analysis_completed_at :datetime 21 | # peak_freq :float 22 | # peak_freq_data :text 23 | # line_type :text 24 | # fprint :integer is an Array 25 | # 26 | 27 | require 'rails_helper' 28 | 29 | RSpec.describe Call, type: :model do 30 | it { should belong_to(:project) } 31 | it { should belong_to(:provider) } 32 | it { should belong_to(:job) } 33 | it { should have_one(:call_medium).dependent(:delete) } 34 | 35 | it "valid record" do 36 | expect(build(:call)).to be_valid 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/models/job_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: jobs 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # project_id :integer not null 9 | # locked_by :string(255) 10 | # locked_at :datetime 11 | # started_at :datetime 12 | # completed_at :datetime 13 | # created_by :string(255) 14 | # task :string(255) not null 15 | # args :binary 16 | # status :string(255) 17 | # error :text 18 | # progress :integer default(0) 19 | # 20 | 21 | require 'rails_helper' 22 | 23 | RSpec.describe Job, type: :model do 24 | it { should belong_to(:project) } 25 | it { should have_many(:calls) } 26 | 27 | it { should validate_presence_of(:project_id) } 28 | 29 | it "valid record" do 30 | expect(build(:job)).to be_valid 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/models/line_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: lines 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # number :text not null 9 | # project_id :integer not null 10 | # line_type :text 11 | # notes :text 12 | # 13 | 14 | require 'rails_helper' 15 | 16 | RSpec.describe Line, type: :model do 17 | it { should belong_to(:project) } 18 | it { should have_many(:line_attributes).dependent(:delete_all) } 19 | 20 | it "valid record" do 21 | expect(build(:line)).to be_valid 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/models/project_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: projects 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # description :text 10 | # included :text 11 | # excluded :text 12 | # created_by :string(255) 13 | # 14 | 15 | require 'rails_helper' 16 | 17 | RSpec.describe Project, type: :model do 18 | it { should have_many(:lines).dependent(:delete_all) } 19 | it { should have_many(:line_attributes).dependent(:delete_all) } 20 | it { should have_many(:calls).dependent(:delete_all) } 21 | it { should have_many(:call_media).dependent(:delete_all) } 22 | it { should have_many(:jobs).dependent(:delete_all) } 23 | 24 | it { should validate_presence_of(:name) } 25 | it { should validate_uniqueness_of(:name) } 26 | 27 | it "valid record" do 28 | expect(build(:project)).to be_valid 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/models/provider_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: providers 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # host :text not null 10 | # port :integer not null 11 | # user :text 12 | # pass :text 13 | # lines :integer default(1), not null 14 | # enabled :boolean default(TRUE) 15 | # 16 | 17 | require 'rails_helper' 18 | 19 | RSpec.describe Provider, type: :model do 20 | ## TODO determine if association is unecessary 21 | # the DialResult model does not exist 22 | #it { should have_many(:dial_results) } 23 | 24 | it { should validate_presence_of(:name) } 25 | it { should validate_presence_of(:host) } 26 | it { should validate_presence_of(:port) } 27 | it { should validate_presence_of(:user) } 28 | it { should validate_presence_of(:pass) } 29 | it { should validate_presence_of(:lines) } 30 | it { should validate_numericality_of(:port).is_less_than(65536).is_greater_than(0) } 31 | it { should validate_numericality_of(:lines).is_less_than(255).is_greater_than(0) } 32 | 33 | it "valid record" do 34 | expect(build(:provider)).to be_valid 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/models/settings_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: settings 4 | # 5 | # id :integer not null, primary key 6 | # var :string(255) not null 7 | # value :text 8 | # thing_id :integer 9 | # thing_type :string(30) 10 | # created_at :datetime 11 | # updated_at :datetime 12 | # 13 | 14 | require 'rails_helper' 15 | 16 | RSpec.describe Settings, type: :model do 17 | it "valid record" do 18 | expect(build(:setting)).to be_valid 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/models/signature_fp_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SignatureFp, type: :model do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/signature_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: signatures 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime 7 | # updated_at :datetime 8 | # name :text not null 9 | # source :string(255) 10 | # description :text 11 | # category :string(255) 12 | # line_type :string(255) 13 | # risk :integer 14 | # 15 | 16 | require 'rails_helper' 17 | 18 | RSpec.describe Signature, type: :model do 19 | ## TODO association may not be needed 20 | # causes crash: PG::UndefinedTable: ERROR: relation "signature_fps" does not exist 21 | #it { should have_many(:signature_fps) } 22 | 23 | it "valid record" do 24 | expect(build(:signature)).to be_valid 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # login :string(255) not null 7 | # email :string(255) 8 | # crypted_password :string(255) not null 9 | # password_salt :string(255) not null 10 | # persistence_token :string(255) not null 11 | # single_access_token :string(255) not null 12 | # perishable_token :string(255) not null 13 | # login_count :integer default(0), not null 14 | # failed_login_count :integer default(0), not null 15 | # last_request_at :datetime 16 | # current_login_at :datetime 17 | # last_login_at :datetime 18 | # current_login_ip :string(255) 19 | # last_login_ip :string(255) 20 | # created_at :datetime 21 | # updated_at :datetime 22 | # enabled :boolean default(TRUE) 23 | # admin :boolean default(TRUE) 24 | # 25 | 26 | require 'rails_helper' 27 | 28 | RSpec.describe User, type: :model do 29 | it { should validate_length_of(:password).is_at_least(8) } 30 | it { should validate_length_of(:password_confirmation).is_at_least(8) } 31 | 32 | it 'valid record' do 33 | expect(build(:user)).to be_valid 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../../config/environment', __FILE__) 4 | # Prevent database truncation if the environment is production 5 | abort("The Rails environment is running in production mode!") if Rails.env.production? 6 | require 'spec_helper' 7 | require 'rspec/rails' 8 | # Add additional requires below this line. Rails is not loaded until this point! 9 | 10 | require 'capybara/rails' 11 | 12 | # Requires supporting ruby files with custom matchers and macros, etc, in 13 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 14 | # run as spec files by default. This means that files in spec/support that end 15 | # in _spec.rb will both be required and run as specs, causing the specs to be 16 | # run twice. It is recommended that you do not name files matching this glob to 17 | # end with _spec.rb. You can configure this pattern with the --pattern 18 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 19 | # 20 | # The following line is provided for convenience purposes. It has the downside 21 | # of increasing the boot-up time by auto-requiring all files in the support 22 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 23 | # require only the support files necessary. 24 | # 25 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 26 | 27 | # Checks for pending migration and applies them before tests are run. 28 | # If you are not using ActiveRecord, you can remove this line. 29 | ActiveRecord::Migration.maintain_test_schema! 30 | 31 | RSpec.configure do |config| 32 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 33 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 34 | 35 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 36 | # examples within a transaction, remove the following line or assign false 37 | # instead of true. 38 | config.use_transactional_fixtures = true 39 | 40 | # RSpec Rails can automatically mix in different behaviours to your tests 41 | # based on their file location, for example enabling you to call `get` and 42 | # `post` in specs under `spec/controllers`. 43 | # 44 | # You can disable this behaviour by removing the line below, and instead 45 | # explicitly tag your specs with their type, e.g.: 46 | # 47 | # RSpec.describe UsersController, :type => :controller do 48 | # # ... 49 | # end 50 | # 51 | # The different available types are documented in the features, such as in 52 | # https://relishapp.com/rspec/rspec-rails/docs 53 | config.infer_spec_type_from_file_location! 54 | 55 | # Filter lines from Rails gems in backtraces. 56 | config.filter_rails_from_backtrace! 57 | # arbitrary gems may also be filtered via: 58 | # config.filter_gems_from_backtrace("gem name") 59 | end 60 | -------------------------------------------------------------------------------- /spec/support/auth_logic_helpers.rb: -------------------------------------------------------------------------------- 1 | module Authlogic 2 | module TestHelper 3 | def create_user_session(user) 4 | visit login_path 5 | within "form#new_user_session" do 6 | expect(page).to have_content "Username" 7 | expect(page).to have_content "Password" 8 | fill_in "user_session_login", with: user.login 9 | fill_in "user_session_password", with: user.password 10 | click_button "Sign in" 11 | end 12 | end 13 | end 14 | end 15 | 16 | # Make this available to just the request and feature specs 17 | RSpec.configure do |config| 18 | config.include Authlogic::TestHelper, type: :request 19 | config.include Authlogic::TestHelper, type: :feature 20 | end 21 | -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryGirl::Syntax::Methods 3 | end 4 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapid7/warvox/094c4d14cd5ff2362ccb462967263dbe8894ab99/tmp/.keep --------------------------------------------------------------------------------