====== Integrate Linux Shell Apps ======

Linux shell applications can be added to Synchronet by using the built in js function bbs.exec(); and must pass
special command line arguments that instruct sbbs to pipe the shell i/o to the client terminal.

  ie: bbs.exec('links2 "google.com/search?q='+google+'"', EX_STDIO|EX_NATIVE|EX_NOLOG);

This method can also be used to launch DOS/Windows executibles as well!

===== Links2 Web Browser =====
links2 is an https compliant graphical text mode www browser typically run from in linux shell.  
For use with Synchronet we won't configure the graphics card and/or image support, so the
output is monochrome and behaves more like a gopher client with enhanced features.

On a Debian system, make sure links2 is installed:
<code>
  $ sudo apt-get install links2
</code>

=== Links2 Security Considerations ===

:!: Note that links2 can give access to the host OS, such as with "OS Shell" from the File menu, which gives the BBS user a shell as the OS user the BBS runs as. At the very least this will likely give someone access to your [[util:scfg|scfg]] tool.

Using the script below, anyone with a sysop [[access:level|security level]] will have access to the full Links2 experience, including running an OS shell as whatever user the BBS runs as. The code uses the is_sysop check, which will return true if the user's security level is 90-99 ([[https://synchro.net/docs/jsobjs.html|Object reference]]).

Anyone without a sysop security level will run Links2 with the "-anonymous" switch, which disables numerous features, including the ability to use "OS Shell" from the menu.

<code>
-anonymous
Restrict links so that it can run on an anonymous account. No local file browsing. No downloads. Executing of viewers is allowed, but user can't add or modify entries in association table.
</code>

This may be sufficient to secure the BBS' host system, but the user will still be allowed to browse the web from your system, potentially doing questionable things that could be attributed to your IP address.

=== Add links2 to SBBS ===

Add links2 to sbbs as an external program as any other door:

<code>
[Links2 Web Browser]
 1: Name                       Links2 Web Browser
 2: Internal Code              LINKS2
 3: Start-up Directory
 4: Command Line               ?links2.js
 5: Clean-up Command Line
 6: Execution Cost             None
 7: Access Requirements
 8: Execution Requirements
 9: Multiple Concurrent Users  Yes
10: Intercept I/O              No
11: Native Executable/Script   No
12: Use Shell or New Context   No
13: Modify User Data           No
14: Execute on Event           No
15: Pause After Execution      No
16: BBS Drop File Type         None
17: Place Drop File In         Node Directory
18: Time Options...

Which (Help or Quit):           
</code>

=== Add to Synchronet Command Shell ===

edit your *.src [[custom:command_shell|command shell]] script and add:
  ie: cmdkey /B
          exec_bin "links2.js"
          end_cmd

and recompile
  /sbbs/exec $ ./baja myshell

add option to your menu *.ans; *.msg; *.asc, etc as appropriate.


=== links2 example script ===
<code>load('sbbsdefs.js');
// links2.js

console.putmsg('\r\n\1n \1glinks v2.18 - text mode www browser\r\n\r\n');
console.putmsg(' \1gPressing [ESC] in Links2 for Menu or read Links2 in Text Section\r\n\r\n');
console.putmsg(' \1h\1yG\1n\1google Search  \1h\1gE\1n\1gnter URL \1h\1wor  \1rQ\1guit \1w');
var ch = console.getkeys("GEQ",K_UPPER);
if(ch === '') ch = 'G';

switch (ch) {
        case 'G':
                console.putmsg('\r\n \1gGoogle search term? \1h\1g');
                var google = console.getstr();
                if(google.length<1)
                        console.putmsg('\r\n\r\n\1g Search term omited, using default\r\n')
                else
                        console.putmsg('\r\n\r\n\1n \1gGoogling \1h\1y'+google+'\r\n');
                console.putmsg(' \1h\1yQ\1n\1guits the browser.\r\n\r\n');
                console.pause();
                google = google.replace(' ', '+');
                if(user.is_sysop)
                    bbs.exec('links2 "google.com/search?q='+google+'"', EX_STDIO|EX_NATIVE|EX_NOLOG)
                else
                    bbs.exec('links2 -anonymous "google.com/search?q='+google+'"', EX_STDIO|EX_NATIVE|EX_NOLOG);
                console.putmsg('\r\n\r\n\1g  Returning to '+system.name);
                mswait(1000);
                exit();
        break;

        case 'E':
                console.putmsg('\r\n\r\n \1gEnter URL: \1h\1g');
                var url = console.getstr();
                var v =  url.indexOf(".")
                if (v < 0) {
                        url = 'http://www.aboutlinux.info/2007/02/links2-cross-platform-console-based-web.html';
                }                
                console.putmsg('\r\n\r\n\1n \1gLoading links2 \r\n');
                console.putmsg(' \1h\1yQ\1n\1guits the browser.\r\n\r\n');
                console.pause();
                url = url.replace(' ', '+');
                
                if(user.is_sysop)
                    bbs.exec('links2 "'+url+'"', EX_STDIO|EX_NATIVE|EX_NOLOG)
                else
                    bbs.exec('links2 -anonymous "'+url+'"', EX_STDIO|EX_NATIVE|EX_NOLOG);
                console.putmsg('\r\n\r\n\1g  Returning to '+system.name);
                mswait(1000);
                exit();
        break;

        case 'Q':
                console.putmsg('\r\n\1g  Returning to '+system.name);
                mswait(1000);
                exit();
        break;
}
</code>

===== WeeChat IRC Client =====

WeeChat (Wee Enhanced Environment for Chat) makes for a decent IRC client alternative to the stock irc.js client.

First, install Weechat under Debian type:

  $ sudo apt-get install weechat
  
add WeeChat as an external program similar to Links2 
( the script's bbs.exec('whatever', <args>); are the same to redirect i/o so no need to add it in SCFG )

add a cmdkey ... end_cmd entry in your command shell similar to links2 above, if desired.

=== example weechat js ===

Weechat looks for a configuration file in /home/<user>/.weechat/irc.conf to get the username/channel/etc information
so wee need to setup a new config for each user/connection, to do this we apply some hackery:

<code>
load("sbbsdefs.js");

var enable;
var server;
var port;
var chan;
var lusr = user.alias;

var quit_msg = system.name+' - WeeChat 1.6 Linux'; // todo random quit messages

var irc = load("modopts.js", "weechat");

if(irc.enabled == undefined) enabled = true;
    else enabled = irc.enabled;
if(irc.chan === undefined) chan = '#synchronet';
    else chan = irc.chan;
if(irc.server === undefined) servers = 'irc.synchro.net';
    else server = irc.servers;

var infile = system.ctrl_dir+'irc-weechat.conf';
var outfile = system.ctrl_dir+'irc-weechat.tmp';
var weechat = '/home/sbbs/.weechat/irc.conf';
var wc_bup = '/home/sbbs/.weechat/irc-'+strftime("%Y-%m%d_%H%M", time())+'.conf';
var save_prefs = system.data_dir+'wc-prefs.'+user.number;

if(!file_exists(weechat) || !enabled) {
        if(!enabled) {
            writeln('\r\n\r\n\1rWeechat is disabled\r\n\r\n');
        } else {
            writeln('\r\n\r\n\1rirc.conf missing.\r\n');
            mswait(500);
            writeln('\1cTrying default irc client\r\n\r\n');
            load("irc.js");
            exit();
        }
}

var line;
var count;

var inf = new File(infile);
var out = new File(outfile);

writeln('\r\n\r\n \1gLoading IRC WeeChat 1.6\r\n');
writeln('\1g AlleyCat! BBS attempts connect to: \r\n');
writeln('   \1h\1yServer:  \1w'+servers);
writeln('   \1h\1yChannel: \1w'+chan);
writeln('   \1h\1yNick:    \1w'+lusr+'\r\n\r\n');

var cont = console.noyes('Would you like to Connect to IRC ');
if(cont) {
    console.putmsg('\r\n\1r Aborting...\r\n\r\n');
    exit(0);
}

console.putmsg('\r\n \1g[\1h\1yC\1g] ontinue to IRC   [\1wE\1g] dit Connection   [\1wQ\1g] uit\r\n\r\n Whadda ya wanna do? ');
var goon = console.getkey().toUpperCase();
if(goon != "Q" && goon != "E") goon = "C";
switch(goon) {
    case 'Q':
        writeln(' Returning to '+system.name+' \r\n\r\n');
        mswait(1000);
        exit();
    break;

    case 'E':
        console.putmsg('\r\n\r\n \1gChannel: \1w');
        var chan1 = console.getstr();
        if(chan1 != '')
            chan = chan1;
        console.putmsg('\1g Nick: \1w');
        var lusr1 = console.getstr();
        if(lusr1 != '')
            lusr = lusr1;
        console.putmsg('\r\n\1gUsing nick: \1h\1y'+lusr+' \1gand joining channel \1h\1y'+chan+'\r\n');
        mswait(1000);
    break;
}

inf.open("r");
out.open("w+");

count = 0;

while(!inf.eof) {
        line = inf.readln().trim();
        if(line.toUpperCase() !== "[SERVER]" && line !== null) { // dump the default settings ...
                out.writeln(line);
                count++;
        }
        else { // found [server] , this section needs to be replace with sbbs user credentials
            out.writeln('[server]');
            out.writeln('synchronet.proxy');
            out.writeln('synchronet.ipv6');
            out.writeln('synchronet.ss');
            out.writeln('synchronet.ssl_cert');
            out.writeln('synchronet.ssl_priorities');
            out.writeln('synchronet.ssl_dhkey_size');
            out.writeln('synchronet.ssl_fingerprint');
            out.writeln('synchronet.ssl_verify');
            out.writeln('synchronet.password');
            out.writeln('synchronet.capabilities');
            out.writeln('synchronet.sasl_mechanism');
            out.writeln('synchronet.sasl_username');
            out.writeln('synchronet.sasl_password');
            out.writeln('synchronet.sasl_key');
            out.writeln('synchronet.sasl_timeout');
            out.writeln('synchronet.sasl_fail');
            out.writeln('synchronet.autoconnect = on');
            out.writeln('synchronet.autoreconnect');
            out.writeln('synchronet.autoreconnect_delay');
            out.writeln('synchronet.nicks_alternate');
            out.writeln('synchronet.local_hostname');
            out.writeln('synchronet.command');
            out.writeln('synchronet.command_delay');
            out.writeln('synchronet.autojoin = "'+chan+'"');
            out.writeln('synchronet.autorejoin ');
            out.writeln('synchronet.autorejoin_delay');
            out.writeln('synchronet.connection_timeout');
            out.writeln('synchronet.anti_flood_prio_high');
            out.writeln('synchronet.anti_flood_prio_low');
            out.writeln('synchronet.away_check');
            out.writeln('synchronet.away_check_max_nicks');
            out.writeln('synchronet.msg_kick');
            out.writeln('synchronet.msg_part');
            out.writeln('synchronet.sasl_timeout');
            out.writeln('synchronet.sasl_fail');
            out.writeln('synchronet.autoconnect = on');
            out.writeln('synchronet.autoreconnect');
            out.writeln('synchronet.autoreconnect_delay');
            out.writeln('synchronet.nicks_alternate');
            out.writeln('synchronet.local_hostname');
            out.writeln('synchronet.command');
            out.writeln('synchronet.command_delay');
            out.writeln('synchronet.autojoin = "'+chan+'"');
            out.writeln('synchronet.autorejoin ');
            out.writeln('synchronet.autorejoin_delay');
            out.writeln('synchronet.connection_timeout');
            out.writeln('synchronet.anti_flood_prio_high');
            out.writeln('synchronet.anti_flood_prio_low');
            out.writeln('synchronet.away_check');
            out.writeln('synchronet.away_check_max_nicks');
            out.writeln('synchronet.msg_kick');
            out.writeln('synchronet.msg_part');
            out.writeln('synchronet.msg_quit = "' + quit_msg+'"');
            out.writeln('synchronet.notify');
            out.writeln('synchronet.nicks = "' + lusr + ',' + lusr+'_,' + '_' + lusr + '_"');
            out.writeln('synchronet.username = "' + lusr.toLowerCase() + '"');
            out.writeln('synchronet.realname = "' + lusr + ' @ ' + system.name + ' [WeeChat 1.6]"');
            out.writeln('synchronet.addresses = "' + servers + '"');
            out.writeln();
            count += 41;
            console.pause();
            out.close(); // yes, [server] is actually the last [section] in weechat irc.conf
        }
}

inf.close();
out.close();
file_rename(weechat, wc_bup);
file_copy(outfile, weechat);
bbs.exec('weechat', EX_STDIO|EX_NATIVE|EX_NOLOG); // execute bash>weechat redirecting stdout back to sbbs

console.pause();
writeln('\r\n\r\n \1gReturning to \1c'+system.name);
mswait(1000);
</code>
===== =====

===== See Also =====
  * [[:howto:|howto index]]

{{tag>}}
