XRpi interfacing with LinFBB

Software is available on this link

Wow, found great software. Yes I know, it has been around for a while. I thought it would be nice to keep me busy on a rainy Saturday. So let’s interface it with LinFBB.

It actually works a bit like BPQ32 and LinFBB with WA8DED emulation. But for XRouter and LinFBB we need a virtual comport. We can make these easily with socat.

sudo apt install socat

Now lets create some comports

# Create pty pair
socat -d -d -ly PTY,link=/home/pi/xrpi/ttyq1 PTY,link=/home/pi/xrpi/ptyq1 &
sleep 2

I have chosen to create this as a normal user and put it in the directory /home/pi/xrpi/. Now as a normal user I can access and use them. I am never much of a fan of doing everything as a Root user.

Now lets configure XRouter.

INTERFACE=5
	TYPE=ASYNC
	COM=/home/pi/xrpi/ttyq1
	PROTOCOL=DEDHOST
	APPLNUM=1
	CHANNELS=4
	SPEED=57600
	FLOW=0
	MTU=256
ENDINTERFACE

APPL=1
	APPLNAME=FBB
	APPLCALL=PI8LAP-1
	APPLALIAS=LAPFBB
	APPLQUAL=254
	APPLFLAGS=4
ENDAPPL

Next is LinFBB… port.sys

# FBB7.0.8-beta8
#
#Ports TNCs
 1     1
#
#Com Interface Adress (Hex)       Baud
1  	 9        /home/pi/xrpi/ptyq1    57600
 
#TNC NbCh Com MultCh Pacln Maxfr NbFwd MxBloc M/P-Fwd Mode  Freq
 0   0    0   0      0     0     0     0      00/01   ----  File-fwd.
1   4    1   1      236   4     1     10     00/15 DUWYL XRpi

Now fist create a start file for XRpi

# First kill running process of socat
sudo kill $(ps aux | grep '[s]ocat' | awk '{print $2}')
# Create pty pair
socat -d -d -ly PTY,link=/home/pi/xrpi/ttyq1 PTY,link=/home/pi/xrpi/ptyq1 &
sleep 2
# Start XRpi(1)
sudo setcap cap_net_raw,cap_net_bind_service=pe xrpi
./xrpi

1. As Normal user, it will need CAP_NET_RAW capability in order to use TCP/IP via the LAN, WiFi or localhost. And it will need CAP_NET_BIND_SERVICE if you wish to open any “service ports” on the linux TCP/IP stack whose numbers are below 1024.

First we have to start XRpi because of course the virtual comports have to be created. After this it is LinFBB’s turn. Now is the time to test.

The link with BPQ32 is also online. Also with virtual comports.

And yes, there is the old trusted LinFBB.

The possibilities are endless with this software. So there goes me free Saturday again.

LinFBB 7.0.10 released

  Released version 7.0.10 of Linfbb. Great news……

Download it at https://ham.packet-radio.net/packet/f6fbb/linux/recent-version/fbb-7.0.10.tar.gz
Source Link  https://sourceforge.net/projects/linfbb/files/fbb-7.0.10.tar.gz/download

Some info from Dave.

Many thanks to Brian (N1URO) for his maintenance scripts contribution and to Paul (G4APL) for his extensive tests and feedback regarding some old bugs and their fixes. Also many thanks to all other sysops using and testing the development version in the SVN repo prior to this release.

Changslog

7.0.10 (Dave van der Locht) Add date 1 Nov 2020
[r187]
- Fixed gateway using wrong FROM callsign with outgoing socket connections.
- Fixed gateway could only use port 1 to 9.
- New 20_epurmess and 20_epurwp maintenance scripts (N1URO).
- Fixed pagination issue with ? command, C (remove paging) didn't work.
- Cleaned obsolete code, fast_fwd was hard set to 1 in init.c but only used 
  in some 'if' statements. 
- Corrected satdoc.c line 384 gcc compiler warning (-Wstringop-overflow)
- Corrected behaviour of /K and /L sysop commands
- Fixed buffer overflow possibility in ibm.c getcurdir()
- Corrected several misleading indentations
- Cleaned code and comments in xfbbd.c
[r188]
- Fixed problem where inbound connections were disconnected after connect.
  with some port types when port in port.sys was higher than 9.
- Commented debugging printf code in the call_nbdos() function.
- Changed version number to 7.0.10.
[r189]
- Set SVN file properties accordingly for executable files.
- Placed autogen.sh script back in the SVN repo.
[r190]
- Fixed gateway J command only could show port 1 to 9 heard lists.
- Fixed mailbox J# command only could handle J1 to J8 (numeric) ports.
- Extended mailbox J# command (letters) a bit.
[r191]
- Detected and corrected some character encoding problems in tnc.c file.
[r192]
- Removed autotool generated files from SVN repo.
[r193]
- Accidentally removed Makefile.am. Placed back into SVN repo.
[r194]
- Fixed filename not exists error when using YAPP download command (YD).
[r195]
- Fixed housekeeping routines crashing on several newer Linux distributions.
- Changed src/Makefile.am, the -fstack-check flag conflicts with
  -fstack-clash-protection which is included by default when GCC is built with
  stack smashing protection (SSP).
- Changed README to reflect correct mailing list e-mail address.
*******************************************************************************

FBB Extensions to YAPP-protocol

These extensions are used in TPK 1.65a (and up) and FBB 5.14 and up.

HEADER extension.

 The header now carries the DATE and TIME of the file being transmitted.
[SOH] [Len] [Filename] [NUL] [File Size] [NUL] [Date] [Time] [NUL]

 The Date and Time are the values reported by DOS, coded in 4 hexadecimal
digits and are sent in ASCII (8 characters).

 The receiver has the choice of using either extended Yapp with checksum or
normal Yapp.

 The normal Yapp reply is RF, as before and the receiver can keep the date
and time information .

 The extended Yapp reply is : RT  Receive_TPK  and is coded : [ACK] [ACK]

 If the receiver reply is RT the protocol used will be what I have called
YappC for Yapp with checksum. When the sender gets this packet he MUST use
YappC.

Data Packets extension.

 If the receivers reply is RT the protocol used will be YappC. The checksum
allows detection of packets corrupted along the link, particularly on the
RS232 lines where there is no error control or correction (or it's very
poor!)

Data packets : [STX] [Len] [Data] [Checksum]

 Checksum is the sum of all data bytes anded with FF in 8 bits like Xmodem.

 If the checksum is bad then the receiver must send a Cancel and enters CW
state.

Crash Recovery.

 A new field has been added to the resume reply to tell the sender if the
receiver can use YappC or not. Resume is sent instead of RF (or RT).

 Resume reply for Yapp: (as used before by TPK and FBB)

    [NAK] [Len] [R] [NUL] [Received Length] [NUL]
            I    I                I
            I    I                +-- in ASCII as in the header
            I    +-- as Resume !
            I
            +------- len of the following bytes

Resume reply for YappC:

    [NAK] [Len] [R] [NUL] [Received Length] [NUL] [C] [NUL]
                                                   I
                 Tells sender I can use YappC -----+

 When the sender gets this packet then he must also use YappC.

FBB Unproto message lists

 FBB software allows sending unproto lists of message. This is validated
separately on each port (letter L in PORT.SYS). Unproto address is FBB with
the following header :

fm F6FBB-1 to FBB ctl UI

 An unproto list line is sent on every validated port each time a message is
recorded. The line is in the form :

12345 B 2053 TEST@ALL F6FBB 920325 This is the subject

 If a message number is missing or does not exist the line will be :

12346 #

 This allows a system listening to the UI packets on a frequency to create a
list identical to the one of the BBS, and then the user will not have to
connect the BBS to know the list of messages and bulletins.

 A control can be done on the number of the messages to check if a line is
missing.

 If the remote system receives a new line, and a line is missing, it only has
to send an unproto frame addressed to the BBS callsign like this :

fm FC1EBN-3 to F6FBB-1 ctl UI
? 00002EE00E

 This will be taken in account only if the user has the U flag validated (EU
command).

 If the user has not his flag validated in the BBS, he will receive a line
like :

fm F6FBB-1 to FBB ctl UI
12200 / callsign

 In this case, the the remote software MUST stop asking unprotoes.

 The first 8 digits are the hexadecimal number of the requested start of the
list (here 00002EE0 -> 12000) and the last two digits are the sum of the four
bytes anded with FF (0E).

 The BBS will then starts sending lines from the requested number up to the
last message number.

 If the number requested seems to be too far from the current line, the BBS
can readjust the request of "callsign" while sending :

fm F6FBB-1 to FBB ctl UI
12200 ! CALLSIGN
12201 B 2040    TEST@FRA    F6FBB  920325 This is a bulletin
12202 #
12203 P 206    F6ABJ@F6ABJ  F6FBB  920325 Hello Remy.
etc...

and then starts sending lines from 12201. The remote system must change its
base number to 12201.

  If the number requested is greater than the last message received in the
BBS, the BBS will send a line like :

 12300 !!

 This indicates that the list in the remote system is up to date. The last
received message in the BBS is 12300.


 The remote system can also connect the BBS and ask for messages in binary
compressed mode using the following sequence :


     BBS                                    Remote system
--------------------------------------------------------------------------
                                            Connection request
Connection.

[FBB-5.14-ABFHM$]
Welcome in Toulouse, Gerard.
F6FBB BBS>
                                            [TPK-1.80-$]
1>
                                            F< #Msg
Binary compressed message #Msg
is sent using format described
in appendix 7
1>
                                            Disconnect.

 From TPK version 1.80 (packet communication program developed by FC1EBN),
this protocol is implemented.

FBB PG programs

PG programs development.

 PG programs are in the PG subdirectory. They are small programs allowing
interactivity with the user.

 COM or EXE programs can be called.

 PG programs must be little as the amount of memory is limited and fast
because the multitasking is stopped during its activity.

 To run a PG program and start a session, the user must type the command PG
followed by the name of the program. The PG command alone gives the content
of the PG subdirectory. The PG program is particularly developed for FBB
software but can be an interface to a standard program.

 Each time a complete line (up to the return character) is received, the PG
program is called with some arguments including a level number. This number
is incremented each time the program is called in the same PG session. The
first time the level number will be 0.

 The line arguments given to the PG program are :

- Callsign (format as F6FBB-8).
- Level number (0 is the first time, up to 99).
- Flags of the user (binary number as user`s mask of INIT.SRV).
- Record number of the user in INF.SYS.
- Received data (each word is a new argument).

 The PG program ends with an exit value. This value is very important and
tells the BBS what to do :

 0 : end of session and return to the BBS menu.
 1 : the program will be called again and the level number is incremented.
 2 : the user will be disconnected.
 3 : the receive data will be sent as a BBS command and return to BBS.
 4 : the receive data will be sent as a BBS command, level incremented.
 5 : the program will be called again, but the level is not incremented.

 The data sent by the PG program to the standard output will be sent to the
user. This allows a real interactivity between the user and the PG program.

  Here is an example of a small program :

/*
 * TST_PG.C
 *
 * Little test program of "PG" command for FBB BBS software.
 *
 * (C) F6FBB 1991.
 *
 * FBB software 5.14 and up.
 *
 *
 * This program echoes to the user what he types
 * or executes a BBS command preceded by "CMD"
 * until "BYE" is received
 */

#include <stdio.h>

main(int argc, char **argv)
{
  int  i;
  int  level = atoi(argv[2]);        /* Get level from argument list   */

                                     /* and transform it to integer    */
  if (level == 0) {                  /* Is level equal to 0 ?          */
                                     /* This is the first call         */
    printf("Hello %s, type BYE when you want to stop !\n", argv[1]);
    return(1);                       /* program will be called again   */
  }
  else {
    strupr(argv[5]);                 /* Capitalise the first word      */
    if (strcmp(argv[5], "BYE") == 0) {       /* is BYE received ?      */
      printf("Ok, bye-bye\n");
      return(0);                     /* Yes, go on BBS                 */
    }
    else if (strcmp(argv[5], "CMD") == 0) {  /* is CMD received ?      */
      for (i = 6 ; i < argc ; i++)   /* List line arguments            */
      printf("%s ", argv[i]);        /* sent by user                   */
      putchar('\n');
      for (i = 6 ; i < argc ; i++)   /* List line arguments            */
        printf("%s ", argv[i]);      /* sent by user                   */
      putchar('\n');
      return(4);                     /* Yes, send command              */
    }
    else {
      printf("You told me : ");      /* These are other lines          */
      for (i = 5 ; i < argc ; i++)   /* List line arguments            */
      printf("%s ", argv[i]);        /* sent by user                   */
      putchar('\n');
      return(1);                     /* No, call again program         */
    }
  }
}

FBB Message filtering

 FBB software allows filtering messages. Filtering is not done by the BBS
software but by external programs developed by users.

 When the BBS starts, the M_FILTER does not really need to be there.  But at
the first message, it must exist.  If it does not exist at THAT time, it will
not be checked again.  So if a M_FILTER is added after that, the BBS must be
rebooted for the filter to take effect.

M_FILTER may be interactive and allows to incorporate some features like 
dedicated information for predefined callsigns, password filtering, etc...

 I did not develop such programs, but this is an open door to many
applications.

 The M_FILTER program must be found by the PATH of MsDos. Its extension can
be COM or EXE, and it must be little and fast as multitasking is stopped
during the activity of this program. If this program is not found, it will
not be called until the BBS is rebooted.

 The M_FILTER may also be created as a DLL.  Both for WinFBB and DosFBB (!!).
The filter must be installed in the path (\FBB\BIN) of Dos.

 The message filter is called (if found) each time a message is ready to be
recorded (when Ctrl Z or /EX is received). The decision to validate or not
the message is function of the exit value of the M_FILTER program.

 The M_FILTER program (if found) is called with some arguments including a
level number. This number is incremented each time the program is called in
the same connection session. The first time the level number will be 0.

 The line arguments given to the M_FILTER program are :

- File name including the text of the message.
- Type of the message (P, B, T).
- Sender.
- "To" field.
- Record number of DIRMES.SYS file.

 The M_FILTER program ends with an exit value. This value is very important
and tells the BBS what to do :

 0 : Message is recorded.
 1 : Message is killed   (status = K).
 2 : Message is archived (status = A).
 3 : Message is held     (status = H).


/*
 * M_FILTER.C
 *
 * The message filter MUST be named M_FILTER (COM or EXE).
 *
 * This example only writes its call arguments in the TEST.MES file.
 *
 * It is called with 5 arguments :
 *   File name of the message.
 *   Type .
 *   Sender.
 *   To.
 *   Number of the record in the DIRMES.SYS file.
 *
 * If it returns 0 : The message is accepted.
 *               1 : The message is killed (status K).
 *               2 : The message is archived (status A).
 *
 */

#include <stdio.h>
main(argc, argv)
int  argc;
char **argv;
{
  int  i;
  FILE * fptr = fopen("TEST.MES", "at");

  for (i = 0 ; i < argc ; fprintf(fptr, "%s ", argv[i++]));
  fputc('\n', fptr);

  fclose(fptr);

  return(0);
}

FBB Connections filtering

 software allows filtering on connection. Filtering is not done by the
BBS software but by external programs developed by users.

 When the BBS starts, the C_FILTER does not really need to be there.  But at
the first connect it must exist.  If it does not exist at THAT time, it will
not be checked again.  So if a C_FILTER is added after that, the BBS must be
rebooted for the filter to take effect.

 Connection filter may be interactive and allows to incorporate some features
like dedicated information for predefined callsigns, password filtering,
etc...

 The C_FILTER program must be found by the PATH of MsDos. Its extension can
be COM or EXE, and it must be little and fast as multitasking is stopped
during the activity of this program. If this program is not found, it will
not be called until the BBS is rebooted.

 The C_FILTER may also be created as a DLL.  Both for WinFBB and DosFBB (!!).
The filter must be installed in the path (\FBB\BIN) of Dos.

 When receiving the connection, the C_FILTER program (if found) is called
with some arguments including a level number. This number is incremented each
time the program is called in the same connection session. The first time the
level number will be 0.

 The line arguments given to the C_FILTER program are :

- Callsign (format as F6FBB-8).
- Level number (0 is the first time, up to 99).
- Flags of the user (binary number as user`s mask of INIT.SRV).
- New : Flag indicating if the user is unknown in the BBS database.
- Record number of the user in INF.SYS.
- New: one more parameter before the optional text. It indicates the
  port where was connected the user.
- Received data (in one argument).


 The C_FILTER program ends with an exit value. This value is very important
and tells the BBS what to do :

 Return value (for C_FILTER):
 0 : Connection is accepted
 1 : C_FILTER will called again, level is incremented
 2 : Connection is refused, user is disconnected
 3 : Connection is accepted, but in read-only mode
 4 : Connection is accepted, but messages will be hold.
 100 and up
   : C_FILTER will called again, next level is equal to the return value.




Example of C_FILTERs.  First C_FILTER.EXE, next C_FILTER.DLL:

Example C_FILTER.EXE:

/*
 * C_FILTER.C
 *
 * Connection filter for FBB BBS software. (C) F6FBB 1991.
 *
 */

#include <stdio.h>

/*
 * Connexion filter called for each connection.
 * All datas sent to stdout will be sent to the user.
 *
 * Filter is called with some arguments on the command line :
 * C_FILTER CallSign Level Flags New Record ReceivedData....
 *
 * The return value tells the BBS if C_FILTER must be called again or not
 * 0 if the BBS can go on,
 * 1 if the C_FILTER must be called again
 * 2 if the user must be disconnected.
 *
 * Callsign is in the FORM CALLSIGN-SSID (F6FBB-0).
 *
 * The first time C_FILTER is called Level=0, and then will be incremented
 * each time it is called for the same connection.
 *
 * Flags give the flags of the user (MGPBSXLE) in a bit field. as defined
 * in the INIT.SRV user's mask. (0x80=Modem, 0x40=Guest, etc...)
 *
 * If New=1, then this is the first connection of the user on the BBS.
 * Record is the record number in the INF.SYS file.
 *
 * All other arguments are the words sent by the user
 * (password for instance).
 *
 * The number of arguments is variable and depends of the number of words
 * in the answer of the user.
 *
 */


/*
 * This is only a little example to test the system. It will be called
 * four times and will give the list of arguments.
 *
 * The fourth time, the hand will be given back to the BBS.
 */

main(int ac, char **av)
{
  int i;
  int level = atoi(av[2]);            /* Get level from argument list  */
                                      /* and transform it to integer   */

  if (level == 0) {                   /* Is level equal to 0 ?         */
    printf("Connection line :\n");    /* This is the first call        */
    for (i = 0 ; i < ac ; i++)        /* List line arguments           */
    printf("%s ", av[i]);
    putchar('\n');
    return(1);                        /* C_FILTER must be called again */
  }
  else {
    printf("Following line  :\n");    /* These are other lines         */
    for (i = 0 ; i < ac ; i++)        /* List line arguments           */
      printf("%s ", av[i]);
    putchar('\n');
    if (level == 4)                   /* Is it the last time ?         */
      return(0);                      /* Yes, go on BBS                */
    else
      return(1);                      /* No, call once more C_FILTER   */
  }
}


Next example, C_FILTER.DLL:

#define STRICT
#include <stdio.h>
#include <windows.h>

/*
 *    Code (C) F6FBB 1995-1996
 *
 *  C_FILTER example
 *
 *  svc_main is the only exported function. It must exist as WFBB will
 *  look for it. All filters are in the same format. If not NULL,
 *  r_buf allow to give a direct text back to the BBS
 * The size of the buffer is given by the parameter len.
 *
 *  Answers may also go to stdout (slower...).
 *
 *  This code is only an example and was not fully tested. It could
 *  give problems as I am not an expert !
 *
 * The used compiler is a Borland C++ 4.5
 *
 *  The DLL is not linked with the code, but checked and loaded when
 *  needed by WFBB.
 *
 * This is an example of the C_FILTER.DEF file :
 *
 *  LIBRARY        C_FILTER
 *  DESCRIPTION    'C_FILTER DLL for FBB'
 *  EXETYPE        WINDOWS
 *  CODE           PRELOAD MOVEABLE DISCARDABLE
 *  DATA           PRELOAD MOVEABLE SINGLE
 *  HEAPSIZE       1024
 *
 *
 * Parameters are (for C_FILTER):
 * ac[0] : The name of the filter (C_FILTER)
 * ac[1] : The callsign and SSID
 * ac[2] : Level number (from 0)
 * ac[3] : Flags
 * ac[4] : Boolean for new user
 * ac[5] : Recordnumber in INF.SYS
 * ac[6] : Port number
 * ac[7] : Optionnal Command received
 * r_buf : The buffer to put an answer
 * len   : The size of the answer buffer
 *
 * Return value (for C_FILTER):
 * 0 : Connection is accepted
 * 1 : C_FILTER will called again, level is incremented
 * 2 : Connection is refused, user is disconnected
 * 3 : Connection is accepted, but in read-only mode
 * 4 : Connection is accepted, but messages will be hold.
 * 100 and up
 *   : C_FILTER will called again, next level is equal to the return value.
 *
 */

int _export FAR PASCAL svc_main (int ac, char FAR ** av, char FAR * r_buf, int len)
{
	if (len > 20)
	{
		sprintf (r_buf, "Inside the C_FILTER\r");
	}
	return (0);
}

FBB Programming servers

Programming technics for servers.

 The servers are exec programs (.COM or .EXE). They are compact and fast.
They will work as the function of the messages which are addressed to them.

 They should be compact because the available memory to run their application
is limited (check the information Ok:nnnn in the status window). They should
be fast because they are executed in the MsDos environment which is not
multi-task.

 The programming language can be of any kind provided that it could be
compiled and that it is able to read parameters which are given appended in
the command line.

 I wrote three servers in TurboC but I have no equivalent in TurboPascal or
in TurboBasic, since I usually don't write in these languages. The working
principle remains always the same whatever language is utilized.

 The program is called with the following manner from the MsDos (Example for
the REQDIR.COM command) :

 C> REQDIR.COM TEMP.$$$

 TEMP.$$$ is the name of the file in which the message addressed to REQDIR is
located. It is necessary to read the name of this file in the command line,
as the one can change from one call to another.

 The file TEMP.$$$ contains the message with the following format :

 SP REQDIR < F6FBB
 Title of message
 Text of message line 1
 Text of message line 2
 ...
 Text of message last line.
 /EX

 The server should then eventually work as a function of the contents of this
message.

 The server can read and make use of the configuration file of the BBS
software (in particular INIT.SRV) to execute its process.

 If the server generates a return message, it should be APPENDED to the
incoming mail file to the BBS. The name of this file can be found in
INIT.SRV. Take care : it is necessary to open the incoming mail file in
APPEND as to add the answer at the end of the file. If it is not done this
way, the messages which could be waiting in this file are destroyed.

 The incoming mail file is tested each and every minute, except in the case
of the usage of a service, where it is tested right after.

 The format of the messages in the incoming mail file is identical to the
format of the file given to the server. Several messages can be written
sequentially in the file. There should not be blank lines or separations
between the messages. The routing fields (@ field), and the originator (<
field) should mandatory be specified. The originator field is the callsign of
the BBS which is taken from the INIT.SRV file.


 Example of server REQFIL written in C language.

/*
 * REQFIL.C Server example.
 *
 * This server is called with a command line like this :
 *
 * REQFIL.COM FILE
 *
 * FILE is the filename of the message to be answered.
 *
 *
 * This server answers to a message like this :
 *
 * SP REQFIL < FC1EBN
 * TEST.TXT @ F6ABJ
 * Text is not necessary
 * /EX
 *
 * by a message like this
 *
 * # <- This is a local message
 * SP FC1EBN @ F6ABJ < F6FBB <- command line
 * Req File : TEST.TXT <- subject
 * Contents of the file <- text
 * etc.....
 * /EX <- end of text (must be in 1st column)
 *
 * Appent to mail in bbs file.
 *
 *
 * The server receives from FBB software 1 argument :
 *
 * argv[1] = Name of the file including the message received from
 * FBB software.
 *
 * ============================================
 * The server must APPEND its answer to MAIL.IN
 * file to avoid destroying existing mail.
 * ============================================
 *
 * As this server opens the INIT.SRV file, it must be in the same
 * directory.
 *
 */

 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/stat.h>

 /* Offsets of parameters from INIT.SRV */

 #define BBS_CALL 1
 #define USER_DIR 8
 #define MAIL_IN 14

 main(int argc, char **argv)
 {
 #define LINE 80
   int end = 0;
   int index = 0;
   FILE *fptr;
   char buffer[LINE];
   char sender[LINE];
   char route[LINE];
   char file[LINE];
   char bbs_call[LINE];
   char base_dir[LINE];
   char mail_in[LINE];

   if (argc != 2) exit(1);            /* Check the number of arguments */

   /* The first task is to open and then read the message */

   fptr = fopen(argv[1], "rt") ;      /* Open the received message */
   if (fptr == NULL) exit(1);

   fgets(buffer, LINE, fptr);         /* Read the command line */
   sscanf(buffer, "%*s %*s %*s %s\n", sender);

   *file = *route = '\0';
   fgets(buffer, LINE, fptr);         /* Read the subject */
   strupr(buffer);                    /* Capitalize */

   /* Scan dir and route */
   sscanf(buffer, "%[^@\n]%[^\n]", file, route);

   fclose(fptr);                   /* All needed is read in the message */

   /* We must get some informations from the INIT.SRV file */

   fptr = fopen("INIT.SRV", "rt"); /* Open the file */
   if (fptr == NULL) exit(1);

   /* Scan the file to get the requested lines. */
   while (!end) {
     fgets(buffer, LINE, fptr) ;
     if (*buffer == '#') continue; /* Comments ignored */

     switch (++index) {

     case BBS_CALL:
       sscanf(buffer,"%[0-9A-Za-z]", bbs_call);
       break; /* Callsign */

     case USER_DIR:
       sscanf(buffer,"%s\n", base_dir);
       break; /* Users directory */

     case MAIL_IN :
       sscanf(buffer,"%s\n", mail_in);
       end = 1; /* Mail in file */
       break;

     }
   }

   fclose(fptr);

   /* Append the answer to mail in file */
   /* Mail in file is opened in appent text mode */

   if (fptr = fopen(mail_in, "at")) {

     /* Tell that this is a message from this BBS */

     fprintf(fptr, "#\n");

     /* Send command line */
     fprintf(fptr, "SP %s %s < %s\n",
     sender, route, bbs_call);

     /* Send subject and requested file */
     send_file(fptr, base_dir, file);

     /* Send end of message */
     fprintf(fptr, "/EX\n");

     /* That's all ! */
     fclose(fptr);
   }
   exit(0);                                /* Tell BBS all is correct */
 }

 int points(char *ptr)   /* Looks for a ".." sequence in the path */
 {
   while (*ptr) {
     if ((*ptr == '.') && (*(ptr+1) == '.')) return(1);
     ++ptr;
   }
   return(0);          /* ".." not fond ! */
 }

 send_file(FILE *fptr, char *base_dir, char *filename)
 {
 #define BUF_SIZE 1000
   int fd;
   int nb;
   char path[256];
   char buffer[BUF_SIZE];
   char last_char;

   sprintf(path, "%s%s", base_dir, filename);       /* Complete path */

   fprintf(fptr, "ReqFil 1.1 : %s\n", filename);       /* Subject */

   if ((!points(path)) && ((fd = open(path, O_RDONLY | O_TEXT)) != -1)) {
     while (nb = read(fd, buffer, BUF_SIZE)) {
       fwrite(buffer, nb, 1, fptr);
       last_char = buffer[nb-1];
     }
     close(fd);

     /* Be sure /EX will be in first column */
     if (last_char != '\n') fputc('\n', fptr);

   }
   else fprintf(fptr, "File not found !\n");
 }

FBB Wildcards

Replacement characters or wildcards.

Most of the search commands or list commands and some configuration files
as well, accept replacement characters or wildcards.

 Character Replaces
 -----------------------------------------------------------------
 @         a letter
 ?         an alphanum character (letter or number)
 =         a printable character
 #         a numeral character or the # character
 *         a string of printable characters.
 &         a dot followed by printable characters. (equiv to .* )
 -----------------------------------------------------------------