How do you query if a room has a password or not?

I am working on adding the password header so asterisk can connect the user to a password protected room.
Is there any way to query a room to see if there is a password set or not ? I am trying to avoid asking a user through the phone to enter a password if there is not really one.

I was able to figure this out with help of several posts from this forum… its not as straight forward as you think. Even https://meet.jit.si/ doesn’t handle incoming calls with passwords in a good way. So I would say, there is no official solution out there.
Here is my tweak/hackie way of handling incoming calls with password, or calls to meetings that haven’t started yet.

First need to modify prosody’s /yourdomain.cfg.lua and add the muc_size module

VirtualHost "meet.jitsi"
    authentication = "anonymous"
    ssl = {
        key = "/config/certs/meet.jitsi.key";
        certificate = "/config/certs/meet.jitsi.crt";
    }
    modules_enabled = {
        "bosh";
        "pubsub";
        "ping";
        "muc_size";
    }
    c2s_require_encryption = false

Then add the following block of code to mod_muc_size.lua inside prosody’s plugin folder: This code will allow you to curl http://your.jitsi.url:5280/all-rooms?domain=your.jitsi.domain. and get back number of rooms, room names, participants and which room has a password set or not. (you might have to expose port 5280 to make this work.

function handle_get_all_rooms(event)
        if (not event.request.url.query) then
                return { status_code = 400; };
        end

        local params = parse(event.request.url.query);
        local domain_name = params["domain"];

        local raw_rooms = get_raw_rooms(domain_name);

        local rooms_json = array();

        for room in raw_rooms do

                local room_jid = room.jid;
        --      local room_everything = room;
                local participant_count = 0;
                local occupants_json = array();
                local occupants = room._occupants;
                local data = room._data;
                if occupants then
                        for _, occupant in room:each_occupant() do
                                -- filter focus as we keep it as hidden participant
                                if string.sub(occupant.nick,-string.len("/focus"))~="/focus" then
                                        for _, pr in occupant:each_session() do
                                                participant_count = participant_count + 1;
                                                local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or "";
                                                local email = pr:get_child_text("email") or "";
                                                occupants_json:push({
                                                        jid = tostring(occupant.nick),
                                                        email = tostring(email),
                                                        display_name = tostring(nick)});
                                        end
                                end
                        end
                end
                rooms_json:push({
                        jid = room_jid,
                        password = data.password,
                        participant_count = participant_count,
                        participants = occupants_json
                });

        end

        local result_json={
                rooms = rooms_json;
        };
        -- create json response
        return { status_code = 200; body = json.encode(result_json); };
end

function module.load()
    module:depends("http");
        module:provides("http", {
                default_path = "/";
                route = {
                        ["GET room-size"] = function (event) return async_handler_wrapper(event,handle_get_room_size) end;
                        ["GET sessions"] = function () return tostring(it.count(it.keys(prosody.full_sessions))); end;
                        ["GET room"] = function (event) return async_handler_wrapper(event,handle_get_room) end;
                        ["GET all-rooms"] = function (event) return async_handler_wrapper(event,handle_get_all_rooms) end;
                };
        });
end

I am also using a docker version of jitsi & jigasi. I had to add the following lines to the jigasi’s sip-communicator.properties file

#This will prevent call to drop after 32 seconds and avoid calls that keep rining and never connect
net.java.sip.communicator.impl.protocol.sip.SKIP_REINVITE_ON_FOCUS_CHANGE_PROP=true
net.java.sip.communicator.impl.protocol.sip.acc1.JITSI_MEET_ROOM_PASS_HEADER_NAME=X-Room-Name-Pass
org.jitsi.jigasi.MUC_SERVICE_ADDRESS=your.jitsi.domain

To be able to add the dial in numbers and pin system to the meeting rooms, you have to add these lines to web’s config.js file

//you have to come up with your own list of numbers (json format and place it in your webserver under /var/www/html/...here is a json file example https://api.jitsi.net/phoneNumberList
dialInNumbersUrl: 'https://your.jitsi.url/phoneNumberList',
dialInConfCodeUrl:  'https://api.jitsi.net/conferenceMapper',

I am using asterisk 17 (PJSIP conf) in my company with the following architecture
Client cellphone -> PSTN -> Cisco unified callmanager - (trunk) -> asterisk - (sip client) -> jigasi

in your Asterisk server, place the following 2 scripts inside /var/lib/asterisk/agi-bin/

jitsi_curling.sh

#!/bin/bash

jit_rm=$(curl -x http://webproxy.ext.ti.com:80 -v https://Jitsi-api.jitsi.net/conferenceMapper?id=$1 | cut -d \, -f 3 | cut -d \: -f 2 | cut -d \" -f 2 | cut -d \@ -f 1)

echo "SET VARIABLE JITSI \"$jit_rm\" "

jitsi_haspassword.sh

#!/bin/bash

jsonResponse=$(curl --silent --noproxy "*" http://your.jitsi.url:5280/all-rooms?domain=muc.meet.jitsi | jq -r '.rooms[]')

for row in $(echo "${jsonResponse}" | jq -r '. | @base64' ); do
    _jq() {
        echo ${row} | base64 --decode | jq -r ${1}
    }
    entry=$(echo $(_jq '.'))
    for variable in $(echo "${entry}" | jq -r '. | @base64' ); do
        _jq() {
            echo ${variable} | base64 --decode | jq -r ${1}
        }
        roomName=$(echo $(_jq '.jid'))
        if [ ${roomName} == $1"@muc.meet.jitsi" ]; then
            roomPassword=$(echo $(_jq '.password'))
        fi
    done
done

if [ -z "${roomPassword}" ]; then
        roomPassword="ConfDidNotStartYet"
else
    if [ ${roomPassword} == "null" ]; then
        roomPassword="NoPassword"
    fi
fi
echo "SET VARIABLE JITSIPassword ${roomPassword}"

This is how my extensions.conf file looks like. I am using from-external context because calls are coming from call manager, if you only have asterisk, you context will be from-internal and you will have just an extension instead of _[0-9].

extensions.conf

[handler]
exten => addheader,1,Set(PJSIP_HEADER(add,X-Room-Name)=${ARG1})
exten => addheader,n,Set(PJSIP_HEADER(add,X-Room-Name-Pass)=${ARG2})

[from-external]
exten => _[0-9].,1,Set(__RINGTIMER={IF([${DB(AMPUSER/6000/ringtimer)} > 0]?{DB(AMPUSER/6000/ringtimer)}:{RINGTIMER_DEFAULT})})
exten => _[0-9].,n,Playback(conf-getconfno)
;get conference pin
exten => _[0-9].,n(getmeeting),Read(Pin,beep,20)
exten => _[0-9].,n,Verbose(result is: ${Pin})
;Run meeting pin cript
exten => _[0-9].,n,AGI(jitsi_curling.sh,${Pin})
exten => _[0-9].,n,Verbose(Meeting Name: ${JITSI})
exten => _[0-9].,n,GotoIf($[${JITSI} = false}]?invalidnum:checkForPassword)
exten => _[0-9].,n(invalidnum),Playback(conf-invalid)
exten => _[0-9].,n,Goto(getmeeting)
;Run password script
exten => _[0-9].,n(checkForPassword),AGI(jitsi_haspassword.sh,${JITSI})
exten => _[0-9].,n,Verbose(Jitsi Password return: ${JITSIPassword})
exten => _[0-9].,n,GotoIf($[${JITSIPassword} = ConfDidNotStartYet]?hangUpandTryAgain:checkforPassword)
;meeting has a password, check if password matches
exten => _[0-9].,n(checkforPassword),GotoIf($[${JITSIPassword} = NoPassword]?joinmeeting:askForPassword)
exten => _[0-9].,n(askForPassword),Playback(agent-pass)
exten => _[0-9].,n,Read(Password,beep,20)
exten => _[0-9].,n,Verbose(Meeting password: ${Password})
exten => _[0-9].,n,GotoIf($[${JITSIPassword} = ${Password}]?joinmeeting:invalidPassword)
exten => _[0-9].,n(invalidPassword),Playback(vm-invalidpassword)
exten => _[0-9].,n,Goto(askForPassword)
;everything is good, join meeting
exten => _[0-9].,n(joinmeeting),Playback(auth-thankyou)
exten => _[0-9].,n,playback(conf-placeintoconf)
exten => _[0-9].,n,Dial(PJSIP/9729956000,,b(handler^addheader^1(${JITSI},${Password})))
exten => _[0-9].,n(hangUpandTryAgain),Playback(conf-onlyperson)
exten => _[0-9].,n,Playback(hangup-try-again)

Thank you for sharing :slight_smile:

I’m trying to set up this (docker-container). Configs are written/modified. (Sip is already running)
But i get no answer on port 5280.
Is there a another trick to enable it in prosody-container?

Greetings, Peter

have you mapped the ports in docker-compose.yml?

prosody:
    #image: jitsi/prosody
    build: ./prosody
    expose:
        - '5222'
        - '5347'
        - '5280'
    ports:
        - '5222:5222'
        - '5347:5347'
        - '5280:5280'
    volumes:
        - ${CONFIG}/prosody:/config
1 Like

Ok. in default config (git docker image) the mapping ports missing.
Thank you, I’ll test it

# XMPP server
prosody:
    image: jitsi/prosody
    restart: ${RESTART_POLICY}
    expose:
        - '5222'
        - '5347'
        - '5280'
    volumes:
        - ${CONFIG}/prosody:/config
1 Like

Thank you, now the port 5280 answers.

Now i can call the url, but there is a error. Is this a missing modul?

startup warn Attempt to read a non-existent global ‘get_raw_rooms’
stack traceback:
/prosody-plugins/mod_muc_size.lua:209: in function ‘handler’
/prosody-plugins/util.lib.lua:75: in function ‘func’
/usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
runnernDo1PMGs error Encountered error: /prosody-plugins/mod_muc_size.lua:209: attempt to call global ‘get_raw_rooms’ (a nil value)
stack traceback:
/prosody-plugins/mod_muc_size.lua:209: in function ‘handler’
/prosody-plugins/util.lib.lua:75: in function ‘func’
/usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
http.server error Traceback[httpserver]: /usr/lib/prosody/util/async.lua:137: /prosody-plugins/mod_muc_size.lua:209: attempt to call global ‘get_raw_rooms’ (a nil value)
stack traceback:
/prosody-plugins/mod_muc_size.lua:209: in function ‘handler’
/prosody-plugins/util.lib.lua:75: in function ‘func’
/usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
stack traceback:
[C]: in function ‘error’
/usr/lib/prosody/util/async.lua:137: in function ‘handler’
/usr/lib/prosody/util/async.lua:211: in function ‘run’
/prosody-plugins/util.lib.lua:98: in function </prosody-plugins/util.lib.lua:62>
(…tail calls…)
/usr/lib/prosody/util/events.lua:79: in function </usr/lib/prosody/util/events.lua:75>
(…tail calls…)
/usr/lib/prosody/net/http/server.lua:248: in function </usr/lib/prosody/net/http/server.lua:176>
[C]: in function ‘xpcall’
/usr/lib/prosody/net/http/server.lua:108: in function ‘process_next’
/usr/lib/prosody/net/http/server.lua:124: in function ‘success_cb’
/usr/lib/prosody/net/http/parser.lua:177: in function ‘feed’
/usr/lib/prosody/net/http/server.lua:155: in function </usr/lib/prosody/net/http/server.lua:154>
[C]: in function ‘pcall’
/usr/lib/prosody/net/server_epoll.lua:159: in function ‘on’
/usr/lib/prosody/net/server_epoll.lua:348: in function ‘onreadable’
/usr/lib/prosody/net/server_epoll.lua:734: in function </usr/lib/prosody/net/server_epoll.lua:726>
[C]: in function ‘xpcall’
/usr/bin/prosody:76: in function ‘loop’
/usr/bin/prosody:86: in main chunk
[C]: in ?

Did you add the module “muc_size” to meet.jitsi.cfg.lua?
Did you copy the block of code inside mod_muc_size.lua?

If you did all this, please share your meet.jitsi.cfg.lua, mod_muc_lua and docker-compose files

A good test to see if your code deployed correctly is running this command.

docker-compose exec prosody bash -c "cat /prosody-plugins/mod_muc_size.lua"

if you are able to see your custom code inside that file, then it made it to the container. Otherwise, you will have to tweak your docker-compose file

I see that your image is getting build remotely every time.
try changing these lines in your docker-compose file, remove jitsi-meet-cfg/ folder and try again.

XMPP server

prosody:
# image: jitsi/prosody
build: ./prosody
restart: ${RESTART_POLICY}

To set the build option is no good idea. So i lost a part my configuration and bosh take errors (I’ve test it and now i test my vmware-snapshot :wink: ) The configuration-files of prosodoy lies partly outside the Docker-Container. In the /prosody-plugin path is already a plugin mod-muc_size. I can overide it when i connect the bash in container. This works well.
Now i do the muc_size setup again.

1 Like