org.bigbluebutton.api.ParamsProcessorUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.bigbluebutton.api.ParamsProcessorUtil.java

Source

/**
* 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/>.
*
*/

package org.bigbluebutton.api;

import javax.servlet.ServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.bigbluebutton.api.domain.Meeting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

public class ParamsProcessorUtil {
    private static Logger log = LoggerFactory.getLogger(ParamsProcessorUtil.class);

    private final String URLDECODER_SEPARATOR = ",";

    private String apiVersion;
    private boolean serviceEnabled = false;
    private String securitySalt;
    private int defaultMaxUsers = 20;
    private String defaultWelcomeMessage;
    private String defaultWelcomeMessageFooter;
    private String defaultDialAccessNumber;
    private String testVoiceBridge;
    private String testConferenceMock;
    private String defaultLogoutUrl;
    private String defaultServerUrl;
    private int defaultNumDigitsForTelVoice;
    private String defaultClientUrl;
    private String defaultAvatarURL;
    private String defaultConfigURL;
    private int defaultMeetingDuration;
    private boolean disableRecordingDefault;

    private String substituteKeywords(String message, String dialNumber, String telVoice, String meetingName) {
        String welcomeMessage = message;

        String DIAL_NUM = "%%DIALNUM%%";
        String CONF_NUM = "%%CONFNUM%%";
        String CONF_NAME = "%%CONFNAME%%";
        ArrayList<String> keywordList = new ArrayList<String>();
        keywordList.add(DIAL_NUM);
        keywordList.add(CONF_NUM);
        keywordList.add(CONF_NAME);

        Iterator<String> itr = keywordList.iterator();
        while (itr.hasNext()) {
            String keyword = (String) itr.next();
            if (keyword.equals(DIAL_NUM)) {
                welcomeMessage = welcomeMessage.replaceAll(DIAL_NUM, dialNumber);
            } else if (keyword.equals(CONF_NUM)) {
                welcomeMessage = welcomeMessage.replaceAll(CONF_NUM, telVoice);
            } else if (keyword.equals(CONF_NAME)) {
                welcomeMessage = welcomeMessage.replaceAll(CONF_NAME, meetingName);
            }
        }
        return welcomeMessage;
    }

    public void processRequiredCreateParams(Map<String, String> params, ApiErrors errors) {
        // Do we have a checksum? If not, complain.
        if (StringUtils.isEmpty(params.get("checksum"))) {
            errors.missingParamError("checksum");
        }
        /*       
               // Do we have a meeting name? If not, complain.
               String meetingName = params.get("name");
               if (StringUtils.isEmpty(meetingName) ) {
                 errors.missingParamError("name");
               }
        */
        // Do we have a meeting id? If not, complain.
        String externalMeetingId = params.get("meetingID");
        if (StringUtils.isEmpty(externalMeetingId)) {
            errors.missingParamError("meetingID");
        }
    }

    public void updateMeeting(Map<String, Object> updateParams, Meeting existing) {
        // TODO: Assign new values to meeting.
        /*       
               String meetingName = (String) updateParams.get("name");
               if (meetingName != null) {
                  existing.setM("name", meetingName);
               }
              
               String viewerPass = params.get("attendeePW");
               if (! StringUtils.isEmpty(viewerPass) ) {
                  newParams.put("attendeePW", viewerPass);
               }
                   
               String modPass = params.get("moderatorPW"); 
               if (! StringUtils.isEmpty(modPass) ) {
                  newParams.put("moderatorPW", modPass);
               }
                   
               String telVoice = params.get("voiceBridge");
               if (! StringUtils.isEmpty(telVoice) ) {
                  newParams.put("voiceBridge", telVoice);
               }       
                   
               String webVoice = params.get("webVoice");
               if (! StringUtils.isEmpty(webVoice)) {
                  newParams.put("webVoice", webVoice);
               }
                   
               String dialNumber = params.get("dialNumber");
               if (! StringUtils.isEmpty(dialNumber)) {
                  newParams.put("dialNumber", dialNumber);
               }       
                   
               String logoutUrl = params.get("logoutURL"); 
               if (! StringUtils.isEmpty(logoutUrl)) {
                  newParams.put("logoutURL", logoutUrl);
               }   
                   
               String record = params.get("record");
               if (! StringUtils.isEmpty(record)) {
                  newParams.put("record", record);
               }   
                   
               String maxUsers = params.get("maxParticipants");
               if (! StringUtils.isEmpty(maxUsers)) {
                  newParams.put("maxParticipants", maxUsers);
               }   
                   
               String meetingDuration = params.get("duration");
               if (! StringUtils.isEmpty(meetingDuration)) {
                  newParams.put("duration", meetingDuration);
               }
                   
               String welcomeMessage = params.get("welcome");
               if (! StringUtils.isEmpty(welcomeMessage)) {
                  newParams.put("welcome", welcomeMessage);
               }
                     
               // Collect metadata for this meeting that the third-party app wants to store if meeting is recorded.
               Map<String, String> meetingInfo = new HashMap<String, String>();
               for (String key: params.keySet()) {
                  if (key.contains("meta")){
         String[] meta = key.split("_");
         if(meta.length == 2){
            meetingInfo.put(meta[1], params.get(key));
         }
                 }   
               }
            
               if (! meetingInfo.isEmpty()) {
                  newParams.put("metadata", meetingInfo);
               }      
        */
    }

    public Map<String, Object> processUpdateCreateParams(Map<String, String> params) {
        Map<String, Object> newParams = new HashMap<String, Object>();

        // Do we have a meeting name? If not, complain.
        String meetingName = params.get("name");
        if (!StringUtils.isEmpty(meetingName)) {
            newParams.put("name", meetingName);
        }

        String viewerPass = params.get("attendeePW");
        if (!StringUtils.isEmpty(viewerPass)) {
            newParams.put("attendeePW", viewerPass);
        }

        String modPass = params.get("moderatorPW");
        if (!StringUtils.isEmpty(modPass)) {
            newParams.put("moderatorPW", modPass);
        }

        String telVoice = params.get("voiceBridge");
        if (!StringUtils.isEmpty(telVoice)) {
            newParams.put("voiceBridge", telVoice);
        }

        String webVoice = params.get("webVoice");
        if (!StringUtils.isEmpty(webVoice)) {
            newParams.put("webVoice", webVoice);
        }

        String dialNumber = params.get("dialNumber");
        if (!StringUtils.isEmpty(dialNumber)) {
            newParams.put("dialNumber", dialNumber);
        }

        String logoutUrl = params.get("logoutURL");
        if (!StringUtils.isEmpty(logoutUrl)) {
            newParams.put("logoutURL", logoutUrl);
        }

        String record = params.get("record");
        if (!StringUtils.isEmpty(record)) {
            newParams.put("record", record);
        }

        String maxUsers = params.get("maxParticipants");
        if (!StringUtils.isEmpty(maxUsers)) {
            newParams.put("maxParticipants", maxUsers);
        }

        String meetingDuration = params.get("duration");
        if (!StringUtils.isEmpty(meetingDuration)) {
            newParams.put("duration", meetingDuration);
        }

        String welcomeMessage = params.get("welcome");
        if (!StringUtils.isEmpty(welcomeMessage)) {
            newParams.put("welcome", welcomeMessage);
        }

        // Collect metadata for this meeting that the third-party app wants to store if meeting is recorded.
        Map<String, String> meetingInfo = new HashMap<String, String>();
        for (String key : params.keySet()) {
            if (key.contains("meta")) {
                String[] meta = key.split("_");
                if (meta.length == 2) {
                    meetingInfo.put(meta[1], params.get(key));
                }
            }
        }

        if (!meetingInfo.isEmpty()) {
            newParams.put("metadata", meetingInfo);
        }

        return newParams;
    }

    public Meeting processCreateParams(Map<String, String> params) {
        String meetingName = params.get("name");
        if (meetingName == null) {
            meetingName = "";
        }
        String externalMeetingId = params.get("meetingID");

        String viewerPass = processPassword(params.get("attendeePW"));
        String modPass = processPassword(params.get("moderatorPW"));

        // Get the digits for voice conference for users joining through the phone.
        // If none is provided, generate one.
        String telVoice = processTelVoice(params.get("voiceBridge"));

        // Get the voice conference digits/chars for users joing through VOIP on the client.
        // If none is provided, make it the same as the telVoice. If one has been provided,
        // we expect that the users will be joined in the same voice conference.
        String webVoice = params.get("webVoice");
        if (StringUtils.isEmpty(webVoice)) {
            webVoice = telVoice;
        }

        // Get all the other relevant parameters and generate defaults if none has been provided.
        String dialNumber = processDialNumber(params.get("dialNumber"));
        String logoutUrl = processLogoutUrl(params.get("logoutURL"));
        boolean record = processRecordMeeting(params.get("record"));
        int maxUsers = processMaxUser(params.get("maxParticipants"));
        int meetingDuration = processMeetingDuration(params.get("duration"));
        String welcomeMessage = processWelcomeMessage(params.get("welcome"));
        welcomeMessage = substituteKeywords(welcomeMessage, dialNumber, telVoice, meetingName);

        String internalMeetingId = convertToInternalMeetingId(externalMeetingId);

        // Check if this is a test meeting. NOTE: This should not belong here. Extract this out.            
        if (isTestMeeting(telVoice)) {
            internalMeetingId = getIntMeetingIdForTestMeeting(telVoice);
        }

        // Collect metadata for this meeting that the third-party app wants to store if meeting is recorded.
        Map<String, String> meetingInfo = new HashMap<String, String>();
        for (String key : params.keySet()) {
            if (key.contains("meta") && key.indexOf("meta") == 0) {
                String[] meta = key.split("_");
                if (meta.length == 2) {
                    log.debug("Got metadata {} = {}", key, params.get(key));
                    meetingInfo.put(meta[1].toLowerCase(), params.get(key));
                }
            }
        }

        // Create a unique internal id by appending the current time. This way, the 3rd-party
        // app can reuse the external meeting id.
        long createTime = System.currentTimeMillis();
        internalMeetingId = internalMeetingId + '-' + new Long(createTime).toString();

        // Create the meeting with all passed in parameters.
        Meeting meeting = new Meeting.Builder(externalMeetingId, internalMeetingId, createTime)
                .withName(meetingName).withMaxUsers(maxUsers).withModeratorPass(modPass).withViewerPass(viewerPass)
                .withRecording(record).withDuration(meetingDuration).withLogoutUrl(logoutUrl).withTelVoice(telVoice)
                .withWebVoice(webVoice).withDialNumber(dialNumber).withDefaultAvatarURL(defaultAvatarURL)
                .withMetadata(meetingInfo).withWelcomeMessage(welcomeMessage).build();

        String configXML = getDefaultConfigXML();
        meeting.storeConfig(true, configXML);

        return meeting;
    }

    public String getApiVersion() {
        return apiVersion;
    }

    public boolean isServiceEnabled() {
        return serviceEnabled;
    }

    public String getDefaultClientUrl() {
        return defaultClientUrl;
    }

    public String getDefaultConfigXML() {
        return getConfig(defaultConfigURL);
    }

    private String getConfig(String url) {
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod(url);
        String configXML = "";
        try {
            client.executeMethod(get);
            configXML = get.getResponseBodyAsString();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return configXML;
    }

    public String getDefaultConfigURL() {
        return defaultConfigURL;
    }

    public String getDefaultLogoutUrl() {
        if ((StringUtils.isEmpty(defaultLogoutUrl)) || defaultLogoutUrl.equalsIgnoreCase("default")) {
            return defaultServerUrl;
        } else {
            return defaultLogoutUrl;
        }
    }

    public String processWelcomeMessage(String message) {
        String welcomeMessage = message;
        if (StringUtils.isEmpty(message)) {
            welcomeMessage = defaultWelcomeMessage;
        }
        if (!StringUtils.isEmpty(defaultWelcomeMessageFooter))
            welcomeMessage += "<br><br>" + defaultWelcomeMessageFooter;
        return welcomeMessage;
    }

    public String convertToInternalMeetingId(String extMeetingId) {
        return DigestUtils.shaHex(extMeetingId);
    }

    public String processPassword(String pass) {
        return StringUtils.isEmpty(pass) ? RandomStringUtils.randomAlphanumeric(8) : pass;
    }

    public boolean hasChecksumAndQueryString(String checksum, String queryString) {
        return (!StringUtils.isEmpty(checksum) && StringUtils.isEmpty(queryString));
    }

    public String processTelVoice(String telNum) {
        return StringUtils.isEmpty(telNum) ? RandomStringUtils.randomNumeric(defaultNumDigitsForTelVoice) : telNum;
    }

    public String processDialNumber(String dial) {
        return StringUtils.isEmpty(dial) ? defaultDialAccessNumber : dial;
    }

    public String processLogoutUrl(String logoutUrl) {
        if (StringUtils.isEmpty(logoutUrl)) {
            if ((StringUtils.isEmpty(defaultLogoutUrl)) || defaultLogoutUrl.equalsIgnoreCase("default")) {
                return defaultServerUrl;
            } else {
                return defaultLogoutUrl;
            }
        }

        return logoutUrl;
    }

    public boolean processRecordMeeting(String record) {
        // The administrator has turned off recording for all meetings.
        if (disableRecordingDefault) {
            log.info("Recording is turned OFF by default.");
            return false;
        }

        boolean rec = false;
        if (!StringUtils.isEmpty(record)) {
            try {
                rec = Boolean.parseBoolean(record);
            } catch (Exception ex) {
                rec = false;
            }
        }

        return rec;
    }

    public int processMaxUser(String maxUsers) {
        int mUsers = -1;

        try {
            mUsers = Integer.parseInt(maxUsers);
        } catch (Exception ex) {
            mUsers = defaultMaxUsers;
        }

        return mUsers;
    }

    public int processMeetingDuration(String duration) {
        int mDuration = -1;

        try {
            mDuration = Integer.parseInt(duration);
        } catch (Exception ex) {
            mDuration = defaultMeetingDuration;
        }

        return mDuration;
    }

    public boolean isTestMeeting(String telVoice) {
        return ((!StringUtils.isEmpty(telVoice)) && (!StringUtils.isEmpty(testVoiceBridge))
                && (telVoice == testVoiceBridge));
    }

    public String getIntMeetingIdForTestMeeting(String telVoice) {
        if ((testVoiceBridge != null) && (telVoice == testVoiceBridge)) {
            if (StringUtils.isEmpty(testConferenceMock))
                return testConferenceMock;
        }

        return "";
    }

    public boolean isConfigXMLChecksumSame(String meetingID, String configXML, String checksum) {
        if (StringUtils.isEmpty(securitySalt)) {
            log.warn("Security is disabled in this service. Make sure this is intentional.");
            return true;
        }

        String cs = DigestUtils.shaHex(meetingID + configXML + securitySalt);
        log.debug("our checksum: [{}], client: [{}]", cs, checksum);
        System.out.println("our checksum: [" + cs + "] client: [" + checksum + "]");
        if (cs == null || cs.equals(checksum) == false) {
            log.info("checksumError: request did not pass the checksum security check");
            return false;
        }
        log.debug("checksum ok: request passed the checksum security check");
        return true;
    }

    public boolean isChecksumSame(String apiCall, String checksum, String queryString) {
        log.debug("checksum: [{}] ; query string: [{}]", checksum, queryString);

        if (StringUtils.isEmpty(securitySalt)) {
            log.warn("Security is disabled in this service. Make sure this is intentional.");
            return true;
        }

        // handle either checksum as first or middle / end parameter
        // TODO: this is hackish - should be done better
        queryString = queryString.replace("&checksum=" + checksum, "");
        queryString = queryString.replace("checksum=" + checksum + "&", "");
        queryString = queryString.replace("checksum=" + checksum, "");

        log.debug("query string after checksum removed: [{}]", queryString);
        String cs = DigestUtils.shaHex(apiCall + queryString + securitySalt);
        log.debug("our checksum: [{}], client: [{}]", cs, checksum);
        if (cs == null || cs.equals(checksum) == false) {
            log.info("checksumError: request did not pass the checksum security check");
            return false;
        }
        log.debug("checksum ok: request passed the checksum security check");
        return true;
    }

    public boolean isPostChecksumSame(String apiCall, HashMap<String, String[]> params) {
        if (StringUtils.isEmpty(securitySalt)) {
            log.warn("Security is disabled in this service. Make sure this is intentional.");
            return true;
        }

        StringBuffer csbuf = new StringBuffer();
        csbuf.append(apiCall);

        SortedSet<String> keys = new TreeSet<String>(params.keySet());

        boolean first = true;
        String checksum = null;
        for (String key : keys) {
            if (key.equals("checksum")) {
                // Don't include the "checksum" parameter in the checksum
                checksum = params.get(key)[0];
                continue;
            }

            for (String value : params.get(key)) {
                if (first) {
                    first = false;
                } else {
                    csbuf.append("&");
                }
                csbuf.append(key);
                csbuf.append("=");
                String encResult;

                try {
                    // we need to re-encode the values because Grails unencoded it
                    // when it received the 'POST'ed data. Might not need to do in a GET request.
                    encResult = URLEncoder.encode(value, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    encResult = value;
                }

                csbuf.append(encResult);
            }
        }
        csbuf.append(securitySalt);

        String baseString = csbuf.toString();

        // System.out.println( "POST basestring = [" + baseString + "]");

        String cs = DigestUtils.shaHex(baseString);
        //System.out.println("our checksum: [" + cs + "], client: [" + checksum + "]");
        //log.debug("our checksum: [{}], client: [{}]", cs, checksum);

        if (cs == null || cs.equals(checksum) == false) {
            log.info("checksumError: request did not pass the checksum security check");
            return false;
        }
        log.debug("checksum ok: request passed the checksum security check");
        return true;
    }

    /*************************************************
     * Setters
     ************************************************/

    public void setApiVersion(String apiVersion) {
        this.apiVersion = apiVersion;
    }

    public void setServiceEnabled(boolean e) {
        serviceEnabled = e;
    }

    public void setSecuritySalt(String securitySalt) {
        this.securitySalt = securitySalt;
    }

    public void setDefaultMaxUsers(int defaultMaxUsers) {
        this.defaultMaxUsers = defaultMaxUsers;
    }

    public void setDefaultWelcomeMessage(String defaultWelcomeMessage) {
        this.defaultWelcomeMessage = defaultWelcomeMessage;
    }

    public void setDefaultWelcomeMessageFooter(String defaultWelcomeMessageFooter) {
        this.defaultWelcomeMessageFooter = defaultWelcomeMessageFooter;
    }

    public void setDefaultDialAccessNumber(String defaultDialAccessNumber) {
        this.defaultDialAccessNumber = defaultDialAccessNumber;
    }

    public void setTestVoiceBridge(String testVoiceBridge) {
        this.testVoiceBridge = testVoiceBridge;
    }

    public void setTestConferenceMock(String testConferenceMock) {
        this.testConferenceMock = testConferenceMock;
    }

    public void setDefaultLogoutUrl(String defaultLogoutUrl) {
        this.defaultLogoutUrl = defaultLogoutUrl;
    }

    public void setDefaultConfigURL(String defaultConfigUrl) {
        this.defaultConfigURL = defaultConfigUrl;
    }

    public void setDefaultServerUrl(String defaultServerUrl) {
        this.defaultServerUrl = defaultServerUrl;
    }

    public void setDefaultNumDigitsForTelVoice(int defaultNumDigitsForTelVoice) {
        this.defaultNumDigitsForTelVoice = defaultNumDigitsForTelVoice;
    }

    public void setDefaultClientUrl(String defaultClientUrl) {
        this.defaultClientUrl = defaultClientUrl;
    }

    public void setDefaultMeetingDuration(int defaultMeetingDuration) {
        this.defaultMeetingDuration = defaultMeetingDuration;
    }

    public void setDisableRecordingDefault(boolean disabled) {
        this.disableRecordingDefault = disabled;
    }

    public void setdefaultAvatarURL(String url) {
        this.defaultAvatarURL = url;
    }

    public ArrayList<String> decodeIds(String encodeid) {
        ArrayList<String> ids = new ArrayList<String>();
        try {
            ids.addAll(Arrays.asList(URLDecoder.decode(encodeid, "UTF-8").split(URLDECODER_SEPARATOR)));
        } catch (UnsupportedEncodingException e) {
            log.error("Couldn't decode the IDs");
        }

        return ids;
    }

    public ArrayList<String> convertToInternalMeetingId(ArrayList<String> extMeetingIds) {
        ArrayList<String> internalMeetingIds = new ArrayList<String>();
        for (String extid : extMeetingIds) {
            internalMeetingIds.add(convertToInternalMeetingId(extid));
        }
        return internalMeetingIds;
    }

    public Map<String, String> getUserCustomData(Map<String, String> params) {
        Map<String, String> resp = new HashMap<String, String>();

        for (String key : params.keySet()) {
            if (key.contains("userdata") && key.indexOf("userdata") == 0) {
                String[] userdata = key.split("-");
                if (userdata.length == 2) {
                    log.debug("Got user custom data {} = {}", key, params.get(key));
                    resp.put(userdata[1], params.get(key));
                }
            }
        }

        return resp;
    }
}