Merge branch '090-encrypted-recording' into HEAD

Conflicts:
	bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java
	record-and-playback/core/Gemfile
	record-and-playback/deploy.sh
This commit is contained in:
Felipe Cecagno 2015-01-21 15:51:29 -02:00
commit 8e12c909c5
16 changed files with 679 additions and 13 deletions

View File

@ -329,12 +329,12 @@ if [ $REPUBLISH ]; then
fi
if [ $CLEAN ]; then
sudo /etc/init.d/bbb-record stop
sudo /etc/init.d/bbb-record-core stop
for type in $TYPES; do
echo " clearning logs in /var/log/bigbluebutton/$type"
find /var/log/bigbluebutton/$type -name "*.log" -exec sudo rm '{}' \;
done
sudo /etc/init.d/bbb-record start
sudo /etc/init.d/bbb-record-core start
fi
if [ $DELETE ]; then

View File

@ -1516,6 +1516,17 @@ class ApiController {
mkp.yield(item.getExtensions())
}
}
}
download() {
r.getDownloads().each { item ->
format{
type(item.getFormat())
url(item.getUrl())
md5(item.getMd5())
key(item.getKey())
length(item.getLength())
}
}
}
}

View File

@ -70,6 +70,12 @@ public class RecordingServiceHelperImp implements RecordingServiceHelper {
builder.duration(info.getPlaybackDuration())
builder.extension(info.getPlaybackExtensions())
}
builder.download {
builder.format(info.getDownloadFormat())
builder.link(info.getDownloadLink())
builder.md5(info.getDownloadMd5())
builder.key(info.getDownloadKey())
}
Map<String,String> metainfo = info.getMetadata();
builder.meta{
metainfo.keySet().each { key ->
@ -104,7 +110,10 @@ public class RecordingServiceHelperImp implements RecordingServiceHelper {
r.setPlaybackLink(rec.playback.link.text());
r.setPlaybackDuration(rec.playback.duration.text());
r.setPlaybackExtensions(rec.playback.extension.children());
r.setDownloadFormat(rec.download.format.text());
r.setDownloadLink(rec.download.link.text());
r.setDownloadMd5(rec.download.md5.text());
r.setDownloadKey(rec.download.key.text());
Map<String, String> meta = new HashMap<String, String>();
rec.meta.children().each { anode ->
log.debug("metadata: "+anode.name()+" "+anode.text())

View File

@ -29,6 +29,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.*;
import org.bigbluebutton.api.domain.Download;
import org.bigbluebutton.api.domain.Meeting;
import org.bigbluebutton.api.domain.Playback;
import org.bigbluebutton.api.domain.Recording;
@ -364,24 +365,46 @@ public class MeetingService implements MessageListener {
ArrayList<Playback> plays = new ArrayList<Playback>();
plays.add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(),
r.getEndTime(), r.getStartTime()),
r.getPlaybackExtensions()));
if (!r.getPlaybackFormat().isEmpty()) {
plays.add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(),
r.getEndTime(), r.getStartTime()),
r.getPlaybackExtensions()));
}
r.setPlaybacks(plays);
ArrayList<Download> downloads = new ArrayList<Download>();
if (!r.getDownloadFormat().isEmpty()) {
downloads.add(new Download(r.getDownloadFormat(), r.getDownloadLink(),
r.getDownloadMd5(), r.getDownloadKey(),
getDurationRecording(r.getEndTime(), r.getStartTime())));
}
r.setDownloads(downloads);
map.put(r.getId(), r);
} else {
Recording rec = map.get(r.getId());
rec.getPlaybacks().add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(),
r.getEndTime(), r.getStartTime()),
r.getPlaybackExtensions()));
if (!r.getPlaybackFormat().isEmpty()) {
rec.getPlaybacks().add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(),
r.getEndTime(), r.getStartTime()),
r.getPlaybackExtensions()));
}
if (!r.getDownloadFormat().isEmpty()) {
rec.getDownloads().add(new Download(r.getDownloadFormat(), r.getDownloadLink(),
r.getDownloadMd5(), r.getDownloadKey(),
getDurationRecording(r.getEndTime(), r.getStartTime())));
}
}
}
return map;
}
private int getDurationRecording(String end, String start) {
return getDurationRecording("", end, start);
}
private int getDurationRecording(String playbackDuration, String end, String start) {
int duration;
try {

View File

@ -0,0 +1,47 @@
package org.bigbluebutton.api.domain;
public class Download {
private String format;
private String url;
private int length;
private String md5;
private String key;
public Download(String format, String url, String md5, String key, int length) {
this.format = format;
this.url = url;
this.length = length;
this.md5 = md5;
this.key = key;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public String getMd5() {
return md5;
}
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}

View File

@ -36,6 +36,7 @@ public class Recording {
private String endTime;
private Map<String, String> metadata = new HashMap<String, String>();
private ArrayList<Playback> playbacks=new ArrayList<Playback>();
private ArrayList<Download> downloads=new ArrayList<Download>();
//TODO:
private String state;
@ -44,7 +45,11 @@ public class Recording {
private String playbackDuration;
private GPathResult playbackExtensions;
private String downloadLink;
private String downloadFormat;
private String downloadMd5;
private String downloadKey;
public String getId() {
return id;
}
@ -84,7 +89,37 @@ public class Recording {
public void setEndTime(String endTime) {
this.endTime = convertOldDateFormat(endTime);
}
public String getDownloadLink() {
return downloadLink;
}
public void setDownloadLink(String downloadLink) {
this.downloadLink = downloadLink;
}
public String getDownloadFormat() {
return downloadFormat;
}
public void setDownloadFormat(String downloadFormat) {
this.downloadFormat = downloadFormat;
}
public String getDownloadMd5() {
return downloadMd5;
}
public void setDownloadMd5(String downloadMd5) {
this.downloadMd5 = downloadMd5;
}
public String getDownloadKey() {
return downloadKey;
}
public void setDownloadKey(String downloadKey) {
this.downloadKey = downloadKey;
}
public String getPlaybackLink() {
return playbackLink;
}
@ -141,6 +176,14 @@ public class Recording {
this.name = name;
}
public ArrayList<Download> getDownloads() {
return downloads;
}
public void setDownloads(ArrayList<Download> downloads) {
this.downloads = downloads;
}
public ArrayList<Playback> getPlaybacks() {
return playbacks;
}

View File

@ -31,6 +31,7 @@ require 'recordandplayback/deskshare_archiver'
require 'recordandplayback/generators/events'
require 'recordandplayback/generators/audio'
require 'recordandplayback/generators/video'
require 'recordandplayback/generators/mconf_processor'
require 'recordandplayback/generators/matterhorn_processor'
require 'recordandplayback/generators/audio_processor'
require 'recordandplayback/generators/presentation'

View File

@ -0,0 +1,54 @@
# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
require 'rubygems'
require 'fileutils'
require 'builder'
require 'mime/types'
require 'digest/md5'
require 'zip'
module BigBlueButton
class MconfProcessor
def self.zip_directory (directory, zipped_file)
BigBlueButton.logger.info("Task: Zipping directory... #{zipped_file} #{directory}")
#files = [webcam, deskshare, dublincore, manifest]
Zip::File.open(zipped_file, Zip::File::CREATE) do |zipfile|
Dir["#{directory}/**/**"].reject{|f|f==zipped_file}.each do |file|
zipfile.add(file.sub(directory+'/', ''), file)
end
end
end
def self.unzip(unzip_dir, zipfile)
Zip::File.open(zipfile) do |zip_file|
zip_file.each do |f|
f_path=File.join(unzip_dir, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
end
end
end
end
end

View File

@ -38,7 +38,15 @@ function deploy_format() {
done
}
deploy_format "presentation"
RECORDING_SERVER=false
if $RECORDING_SERVER ; then
deploy_format "presentation"
deploy_format "mconf_decrypter"
sudo mv /usr/local/bigbluebutton/core/scripts/mconf-recording-decrypter.initd /etc/init.d/mconf-recording-decrypter
sudo mv /usr/local/bigbluebutton/core/scripts/mconf-recording-decrypter.monit /etc/monit/conf.d/mconf-recording-decrypter
else
deploy_format "mconf_encrypted"
fi
sudo mkdir -p /var/bigbluebutton/playback/
sudo mkdir -p /var/bigbluebutton/recording/raw/

View File

@ -0,0 +1,166 @@
#!/usr/bin/ruby
# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
require '../../core/lib/recordandplayback'
require 'rubygems'
require 'yaml'
require 'net/http'
require 'rexml/document'
require 'open-uri'
require 'digest/md5'
BigBlueButton.logger = Logger.new("/var/log/bigbluebutton/mconf_decrypter.log",'daily' )
#BigBlueButton.logger = Logger.new(STDOUT)
bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
mconf_props = YAML::load(File.open('mconf-decrypter.yml'))
# these properties must be global variables (starting with $)
$private_key = mconf_props['private_key']
$get_recordings_url = mconf_props['get_recordings_url']
$recording_dir = bbb_props['recording_dir']
$raw_dir = "#{$recording_dir}/raw"
$archived_dir = "#{$recording_dir}/status/archived"
def fetchRecordings(url)
#BigBlueButton.logger.debug("Fetching #{url}")
doc = nil
begin
doc = Nokogiri::XML(Net::HTTP.get_response(URI.parse(url)).body)
returncode = doc.xpath("//returncode")
if returncode.empty? or returncode.text != "SUCCESS"
BigBlueButton.logger.error "getRecordings didn't return success:\n#{doc.to_xml(:indent => 2)}"
return false
end
rescue
BigBlueButton.logger.error("Exception occurred: #{$!}")
return false
end
doc.xpath("//recording").each do |recording|
record_id = recording.xpath(".//recordID").text
recording.xpath(".//download/format").each do |format|
type = format.xpath(".//type").text
if type == "encrypted"
meeting_id = record_id
file_url = format.xpath(".//url").text
key_file_url = format.xpath(".//key").text
md5_value = format.xpath(".//md5").text
encrypted_file = file_url.split("/").last
decrypted_file = File.basename(encrypted_file, '.*') + ".zip"
if not File.exist?("#{$archived_dir}/#{meeting_id}.done") then
Dir.chdir($raw_dir) do
BigBlueButton.logger.info("Next recording to be processed is #{meeting_id}")
BigBlueButton.logger.debug("Removing any file previously downloaded related to this recording")
FileUtils.rm_r Dir.glob("#{$raw_dir}/#{record_id}*"), :force => true
BigBlueButton.logger.debug("recordID = #{record_id}")
BigBlueButton.logger.debug("file_url = #{file_url}")
BigBlueButton.logger.debug("key_file_url = #{key_file_url}")
BigBlueButton.logger.debug("md5_value = #{md5_value}")
BigBlueButton.logger.info("Downloading the encrypted file to #{encrypted_file}")
begin
writeOut = open(encrypted_file, "wb")
writeOut.write(open(file_url).read)
writeOut.close
rescue Exception => e
BigBlueButton.logger.error "Failed to download the encrypted file: #{e.to_s}"
next
end
md5_calculated = Digest::MD5.file(encrypted_file)
if md5_calculated == md5_value
BigBlueButton.logger.info("The calculated MD5 matches the expected value")
key_file = key_file_url.split("/").last
decrypted_key_file = File.basename(key_file, '.*') + ".txt"
BigBlueButton.logger.info("Downloading the key file to #{key_file}")
writeOut = open(key_file, "wb")
writeOut.write(open(key_file_url).read)
writeOut.close
if key_file != decrypted_key_file
BigBlueButton.logger.debug("Locating private key")
if not File.exists?("#{$private_key}")
BigBlueButton.logger.error "Couldn't find the private key on #{$private_key}"
next
end
BigBlueButton.logger.debug("Decrypting recording key")
command = "openssl rsautl -decrypt -inkey #{$private_key} < #{key_file} > #{decrypted_key_file}"
status = BigBlueButton.execute(command, false)
if not status.success?
BigBlueButton.logger.error "Couldn't decrypt the random key with the server private key"
next
end
FileUtils.rm_r "#{key_file}"
else
BigBlueButton.logger.info("No public key was used to encrypt the random key")
end
BigBlueButton.logger.debug("Decrypting the recording file")
command = "openssl enc -aes-256-cbc -d -pass file:#{decrypted_key_file} < #{encrypted_file} > #{decrypted_file}"
status = BigBlueButton.execute(command, false)
if not status.success?
BigBlueButton.logger.error "Couldn't decrypt the recording file using the random key"
next
end
BigBlueButton::MconfProcessor.unzip("#{$raw_dir}/#{meeting_id}", decrypted_file)
archived_done = File.new("#{$archived_dir}/#{meeting_id}.done", "w")
archived_done.write("Archived #{meeting_id}")
archived_done.close
[ "#{encrypted_file}", "#{decrypted_file}", "#{decrypted_key_file}" ].each { |file|
BigBlueButton.logger.info("Removing #{file}")
FileUtils.rm_r "#{file}"
}
BigBlueButton.logger.info("Recording #{record_id} decrypted successfully")
else
BigBlueButton.logger.error("The calculated MD5 doesn't match the expected value")
FileUtils.rm_f(encrypted_file)
end
end
end
end
end
end
return true
end
def processGetRecordingsUrlInput(input)
if input.respond_to? "each"
input.each do |url|
processGetRecordingsUrlInput url
end
else
fetchRecordings input
end
end
processGetRecordingsUrlInput $get_recordings_url

View File

@ -0,0 +1,2 @@
get_recordings_url: http://localhost:8080/bigbluebutton/api/getRecordings?checksum=1bc39cd242bb8f9b331af0d2323dc1ba3cabe2fe
private_key: /usr/local/bigbluebutton/core/scripts/private.pem

View File

@ -0,0 +1,44 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: mconf-recording-decrypter
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: Starts the foo service
### END INIT INFO
NAME=mconf-recording-decrypter
PID_FILE=/var/run/mconf-recording-decrypter.pid
DIR=/usr/local/bigbluebutton/core/scripts
EXEC=mconf-decrypter.rb
RUN_AS=tomcat7
if [ ! -f $DIR/$EXEC ]; then
echo "$DIR/$EXEC not found."
exit
fi
case "$1" in
start)
echo "Starting $NAME"
cd $DIR
start-stop-daemon -d $DIR --start --background --pidfile $PID_FILE --chuid $RUN_AS:$RUN_AS --make-pidfile --exec $EXEC --quiet
;;
stop)
echo "Stopping $NAME"
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $EXEC
;;
force-reload|restart)
$0 stop
$0 start
;;
*)
echo "Use: /etc/init.d/$NAME {start|stop|restart|force-reload}"
exit 1
;;
esac
exit 0

View File

@ -0,0 +1,3 @@
check process mconf-recording-decrypter with pidfile /var/run/mconf-recording-decrypter.pid
start program = "/etc/init.d/mconf-recording-decrypter start" with timeout 60 seconds
stop program = "/etc/init.d/mconf-recording-decrypter stop"

View File

@ -0,0 +1,22 @@
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
location /mconf_encrypted {
root /var/bigbluebutton/published;
index index.html index.htm;
}

View File

@ -0,0 +1,62 @@
# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
require '../../core/lib/recordandplayback'
require 'rubygems'
require 'trollop'
require 'yaml'
opts = Trollop::options do
opt :meeting_id, "Meeting id to archive", :default => '58f4a6b3-cd07-444d-8564-59116cb53974', :type => String
end
meeting_id = opts[:meeting_id]
#Mconf process log file
logger = Logger.new("/var/log/bigbluebutton/mconf_encrypted/process-#{meeting_id}.log", 'daily' )
BigBlueButton.logger = logger
# This script lives in scripts/archive/steps while bigbluebutton.yml lives in scripts/
props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
recording_dir = props['recording_dir']
raw_presentation_src = props['raw_presentation_src']
meeting_raw_dir = "#{recording_dir}/raw/#{meeting_id}"
meeting_raw_presentation_dir = "#{raw_presentation_src}/#{meeting_id}"
meeting_process_dir = "#{recording_dir}/process/mconf_encrypted/#{meeting_id}"
if not FileTest.directory?(meeting_process_dir)
FileUtils.mkdir_p "#{meeting_process_dir}"
# Create a copy of the raw archives
BigBlueButton.logger.info("Copying the recording raw files from #{meeting_raw_dir} to #{meeting_process_dir}")
FileUtils.cp_r Dir.glob("#{meeting_raw_dir}/*"), meeting_process_dir
# There's no need to backup the presentation raw folder now
# FileUtils.mkdir_p "#{meeting_process_dir}/presentation_raw"
# BigBlueButton.logger.info("Copying the recording presentation from #{meeting_raw_presentation_dir}/#{meeting_id} to #{meeting_process_dir}/presentation_raw")
# FileUtils.cp_r Dir.glob("#{meeting_raw_presentation_dir}/#{meeting_id}/*"), "#{meeting_process_dir}/presentation_raw"
process_done = File.new("#{recording_dir}/status/processed/#{meeting_id}-mconf_encrypted.done", "w")
process_done.write("Processed #{meeting_id}")
process_done.close
end

View File

@ -0,0 +1,171 @@
# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
require '../../core/lib/recordandplayback'
require 'rubygems'
require 'yaml'
require 'cgi'
require 'digest/md5'
bbb_props = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))
recording_dir = bbb_props['recording_dir']
playback_host = bbb_props['playback_host']
published_dir = bbb_props['published_dir']
raw_presentation_src = bbb_props['raw_presentation_src']
done_files = Dir.glob("#{recording_dir}/status/processed/*.done")
done_files.each do |df|
match = /(.*)-(.*).done/.match df.sub(/.+\//, "")
meeting_id = match[1]
if (match[2] == "mconf_encrypted")
BigBlueButton.logger = Logger.new("/var/log/bigbluebutton/mconf_encrypted/publish-#{meeting_id}.log", 'daily' )
meeting_process_dir = "#{recording_dir}/process/mconf_encrypted/#{meeting_id}"
meeting_publish_dir = "#{recording_dir}/publish/mconf_encrypted/#{meeting_id}"
meeting_published_dir = "#{recording_dir}/published/mconf_encrypted/#{meeting_id}"
meeting_raw_dir = "#{recording_dir}/raw/#{meeting_id}"
meeting_raw_presentation_dir = "#{raw_presentation_src}/#{meeting_id}"
if not FileTest.directory?(meeting_publish_dir)
FileUtils.mkdir_p meeting_publish_dir
Dir.chdir(meeting_publish_dir) do
BigBlueButton::MconfProcessor.zip_directory(meeting_process_dir, "#{meeting_id}.zip")
metadata = BigBlueButton::Events.get_meeting_metadata("#{meeting_process_dir}/events.xml")
length = 16
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'
password = ''
length.times { password << chars[rand(chars.size)] }
passfile = File.new("#{meeting_id}.txt", "w")
passfile.write "#{password}"
passfile.close
# encrypt files
command = "openssl enc -aes-256-cbc -pass file:#{meeting_id}.txt < #{meeting_id}.zip > #{meeting_id}.dat"
status = BigBlueButton.execute(command)
if not status.success?
raise "Couldn't encrypt the recording file using the random key"
end
FileUtils.rm_f "#{meeting_id}.zip"
key_filename = ""
if metadata.has_key?('mconflb-rec-server-key') and not metadata['mconflb-rec-server-key'].to_s.empty?
key_filename = "#{meeting_id}.enc"
# The key is already unescaped in the metadata!!
#BigBlueButton.logger.info("Unescaping public key")
#public_key_decoded = CGI::unescape("#{metadata['public-key'].to_s}")
public_key_decoded = "#{metadata['mconflb-rec-server-key'].to_s}"
public_key_filename = "public-key.pem"
public_key = File.new("#{public_key_filename}", "w")
public_key.write "#{public_key_decoded}"
public_key.close
=begin
# Test: print the public key
public_key = File.new("#{public_key_filename}", "r")
counter = 0
while (line = public_key.gets)
BigBlueButton.logger.info "#{counter}: #{line}"
counter = counter + 1
end
public_key.close
=end
command = "openssl rsautl -encrypt -pubin -inkey #{public_key_filename} < #{meeting_id}.txt > #{meeting_id}.enc"
status = BigBlueButton.execute(command)
if not status.success?
raise "Couldn't encrypt the random key using the server public key passed as metadata"
end
# Comment it for testing
FileUtils.rm_f ["#{meeting_id}.txt", "#{public_key_filename}"]
else
key_filename = "#{meeting_id}.txt"
BigBlueButton.logger.warn "No public key was found in the meeting's metadata"
end
# generate md5 checksum
md5sum = Digest::MD5.file("#{meeting_id}.dat")
BigBlueButton.logger.info("Creating metadata.xml")
# Create metadata.xml
b = Builder::XmlMarkup.new(:indent => 2)
metaxml = b.recording {
b.id(meeting_id)
b.state("available")
b.published(true)
# Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
b.start_time(BigBlueButton::Events.first_event_timestamp("#{meeting_process_dir}/events.xml"))
b.end_time(BigBlueButton::Events.last_event_timestamp("#{meeting_process_dir}/events.xml"))
b.download {
b.format("encrypted")
b.link("http://#{playback_host}/mconf_encrypted/#{meeting_id}/#{meeting_id}.dat")
b.md5(md5sum)
b.key("http://#{playback_host}/mconf_encrypted/#{meeting_id}/#{key_filename}")
}
b.meta {
BigBlueButton::Events.get_meeting_metadata("#{meeting_process_dir}/events.xml").each { |k,v| b.method_missing(k,v) }
}
}
metadata_xml = File.new("metadata.xml","w")
metadata_xml.write(metaxml)
metadata_xml.close
BigBlueButton.logger.info("Publishing mconf_encrypted")
# Now publish this recording
if not FileTest.directory?("#{published_dir}/mconf_encrypted")
FileUtils.mkdir_p "#{published_dir}/mconf_encrypted"
end
BigBlueButton.logger.info("Publishing files")
FileUtils.cp_r(meeting_publish_dir, "#{published_dir}/mconf_encrypted")
# it doesn't work since video and deskshare files are owned by red5,
# freeswitch files are owned by freeswitch, and this script is ran by
# tomcat6, so it can just remove files owned by tomcat6
FileUtils.rm_r [ "/usr/share/red5/webapps/video/streams/#{meeting_id}",
"/usr/share/red5/webapps/deskshare/streams/#{meeting_id}",
Dir.glob("/var/freeswitch/meetings/#{meeting_id}*.wav") ], :force => true
# Remove all the recording flags
FileUtils.rm_f [ "#{recording_dir}/status/sanity/#{meeting_id}.done",
"#{recording_dir}/status/recorded/#{meeting_id}.done",
"#{recording_dir}/status/archived/#{meeting_id}.done" ]
# Comment it for testing
BigBlueButton.logger.info("Removing the recording raw files: #{meeting_raw_dir}")
FileUtils.rm_r meeting_raw_dir, :force => true
BigBlueButton.logger.info("Removing the recording presentation: #{meeting_raw_presentation_dir}")
FileUtils.rm_r meeting_raw_presentation_dir, :force => true
publish_done = File.new("#{recording_dir}/status/published/#{meeting_id}-mconf_encrypted.done", "w")
publish_done.write("Published #{meeting_id}")
publish_done.close
end
end
end
end