{$IFDEF WtrGate}{$IFDEF UseOvr}{$O+,F+}{$ENDIF}{$ENDIF}
UNIT SeenBy;

{$i platform.inc}

{ deze unit bevat alle code om SEEN-BY en PATH regels te bewerken }

INTERFACE

USES Database;

CONST NN_PAIRS_PER_BLOCK = 50;

TYPE SBP_BlockPtr    = ^SBP_BlockRecord;
     SBP_BlockRecord = RECORD
                             Count        : 0..NN_PAIRS_PER_BLOCK;
                             Nets,Nodes   : ARRAY[1..NN_PAIRS_PER_BLOCK] OF WORD;
                             NextBlockPtr : SBP_BlockPtr;
                         END;

PROCEDURE SBP_ReleaseSBPBlocks (VAR SBPtr : SBP_BlockPtr);
FUNCTION  SBP_Instanciate (OldSBPtr : SBP_BlockPtr) : SBP_BlockPtr;
FUNCTION  SBP_StoreSeenBysAndPath (FromZone : WORD) : BOOLEAN;
PROCEDURE SBP_StoreEmptySeenByAndPath;
PROCEDURE SBP_ReplaceSeenBysAndPath (VAR AreaRec : AreaBaseRecord);


IMPLEMENTATION

USES Ramon,
     Logs,
     Cfg,
     Msgs;

{

Jos Huijnen:

So in general, only the NODENUMBERS should be added (no stripping of point
numbers to get nodenumbers). And the nodenumbers added to the seen-by are
the ones mentioned in the area configuration (add seen-by), and the ones
mentioned in the export-field. If there are pointnumbers in one or more
of those fields, you just leave them alone (don't do anything with them).
This 'rule' will work from a view of a pointsetup as well from a view of a
nodesetup.


A point(setup) should leave the path alone. The path is only updated by
nodes. If a point is writing a new echomail, then this echomail even has
no path at all (until it is tossed at the points bossnode).

The path is only updated by the system itself. So when 284/312 handles a
message, 284/312 is added to the path. If 284/312.55 is handling a message
it doesn't even touch the path, because 284/312.55 isn't a node, and only
nodes are added to the path.

}


{--------------------------------------------------------------------------}
{ SBP_ReleaseSBPBlocks                                                     }
{                                                                          }
{ This routine releases the memory allocated by the chain of SB blocks.    }
{                                                                          }
PROCEDURE SBP_ReleaseSBPBlocks (VAR SBPtr : SBP_BlockPtr);

VAR ErasePtr : SBP_BlockPtr;

BEGIN
     WHILE (SBPtr <> NIL) DO
     BEGIN
          ErasePtr:=SBPtr;
          SBPtr:=SBPtr^.NextBlockPtr;
          FreeMem (ErasePtr,SizeOf (SBP_BlockRecord));
     END; { while }
END;


{--------------------------------------------------------------------------}
{ SBP_Instanciate                                                          }
{                                                                          }
{ This routine creates a new copy of the SEEN-BY or PATH administration    }
{ block(s) pointed to and returns the pointer to the first entry of the    }
{ new chain. This is used by MsgsPushState.                                }
{                                                                          }
FUNCTION SBP_Instanciate (OldSBPtr : SBP_BlockPtr) : SBP_BlockPtr;

VAR NewSBPtr,
    CurrSBPtr : SBP_BlockPtr;

BEGIN
     SBP_Instanciate:=NIL; { in case there is nothing to copy }
     CurrSBPtr:=NIL;

     WHILE (OldSBPtr <> NIL) DO
     BEGIN
          { allocate a new block }
          GetMem (NewSBPtr,SizeOf (SBP_BlockRecord));

          { copy everything, then set link to next block to NIL  }
          { to avoid linking to an old block (should not happen  }
          { when the entire chain is copied and correctly linked }
          { to the previous list.                                }

          NewSBPtr^:=OldSBPtr^;
          NewSBPtr^.NextBlockPtr:=NIL;

          { if we had a previous block, then link it to the new     }
          { block, otherwise return the pointer to the first block. }
          IF (CurrSBPtr <> NIL) THEN
             CurrSBPtr^.NextBlockPtr:=NewSBPtr
          ELSE
              SBP_Instanciate:=NewSBPtr;

          { make the new block the current block }
          CurrSBPtr:=NewSBPtr;

          { go to the next old block }
          OldSBPtr:=OldSBPtr^.NextBlockPtr;
     END; { while }
END;


{--------------------------------------------------------------------------}
{ AddNetNodePair                                                           }
{                                                                          }
{ This routine adds a net/node pair to the administration. Duplicates are  }
{ removed.                                                                 }
{                                                                          }
PROCEDURE AddNetNodePair (Net,Node : WORD; VAR FirstPtr : SBP_BlockPtr);

VAR LastPtr,
    SBBPtr  : SBP_BlockPtr;
    Lp      : WORD;

BEGIN
     IF (FirstPtr = NIL) THEN
     BEGIN
          GetMem (FirstPtr,SizeOf (SBP_BlockRecord));

          WITH FirstPtr^ DO
          BEGIN
               Count:=1;
               Nets[1]:=Net;
               Nodes[1]:=Node;
               NextBlockPtr:=NIL;
          END; { with }

          Exit; { done }                  { ## EXIT ## }
     END;

     { search against duplicates }

     SBBPtr:=FirstPtr;

     WHILE (SBBPtr <> NIL) DO
     BEGIN
          LastPtr:=SBBPtr;

          WITH SBBPtr^ DO
               FOR Lp:=1 TO Count DO
                   IF (Nets[Lp] = Net) AND (Nodes[Lp] = Node) THEN
                      Exit; { duplicate }  { ## EXIT ## }

          SBBPtr:=SBBPtr^.NextBlockPtr;
     END; { while }

     { LastPtr now points to the block where we will add this pair, }
     { assuming there is place.                                     }
     IF (LastPtr^.Count = NN_PAIRS_PER_BLOCK) THEN
     BEGIN
          GetMem (LastPtr^.NextBlockPtr,SizeOf (SBP_BlockRecord));
          LastPtr:=LastPtr^.NextBlockPtr;

          WITH LastPtr^ DO
          BEGIN
               Count:=0;
               NextBlockPtr:=NIL;
          END;
     END;

     WITH LastPtr^ DO
     BEGIN
          Inc (Count);
          Nets[Count]:=Net;
          Nodes[Count]:=Node;
     END;
END;


{--------------------------------------------------------------------------}
{ ScanAndStore                                                             }
{                                                                          }
{ This routine scans a SEEN-BY or PATH line for node numbers. Found pairs  }
{ are fed to AddNetNodePair for storing.                                   }
{                                                                          }
{ Deze routine scant een SEEN-BY of PATH regel op onze node nummers. Dit   }
{ voorkomt dat we die straks toe gaan zitten voegen en naar die users gaan }
{ exporteren.                                                              }
{ Merk op dat AKAs in de FROM zone voor moeten komen.                      }
{ Each of the AKAs is checked against our nodes and if a match is found,   }
{ this routine returns TRUE. This can be used for circular path detection. }
{                                                                          }
FUNCTION ScanAndStore (Regel        : STRING;
                       KludgeLen    : BYTE;
                       OurNodeCheck : BOOLEAN;
                       VAR FirstPtr : SBP_BlockPtr) : BOOLEAN;

VAR P,PS     : BYTE;
    Part     : STRING[11];
    Net,Node : WORD;
    Nop      : ValNop;
    Lp       : 1..MaxAKAs;

BEGIN
     ScanAndStore:=FALSE;

     Delete (Regel,1,KludgeLen);

     IF (Regel[Length (Regel)] = #13) THEN
        Delete (Regel,Length (Regel),1);

     IF (Regel[1] = ' ') THEN
        Delete (Regel,1,1);

     P:=Pos ('/',Regel);
     PS:=Pos (' ',Regel);

     IF (P = 0) OR ((PS > 0) AND (PS < P)) THEN
     BEGIN
          LogMessage (liGeneral,'[NetNodeScan] Bad: "'+Regel+'"');
          Exit;
     END;

     WHILE (Regel <> '') DO
     BEGIN
          P:=Pos (' ',Regel);

          IF (P > 0) THEN
          BEGIN
               Part:=Copy (Regel,1,P-1);
               Delete (Regel,1,P);
          END ELSE
          BEGIN
               Part:=Regel;
               Regel:='';
          END;

          P:=Pos ('/',Part);
          IF (P > 0) THEN
          BEGIN
               Val (Copy (Part,1,P-1),Net,Nop);
               Delete (Part,1,P);
          END;

          Val (Part,Node,Nop);

          AddNetNodePair (Net,Node,FirstPtr);

          IF OurNodeCheck THEN
             FOR Lp:=1 TO MaxAkas DO
                 {## need to add pointnet behaviour}
                 IF (Config.NodeNrs[Lp].Point = 0) AND
                    (Config.NodeNrs[Lp].Zone = Msg.SBP_Zone) AND
                    (Config.NodeNrs[Lp].Net = Net) AND
                    (Config.NodeNrs[Lp].Node = Node) THEN
                 BEGIN
                      LogMessage (liGeneral,'Found our AKA in PATH: '+Word2String (Net)+'/'+Word2String (Node));
                      ScanAndStore:=TRUE; { found our net/node pair }
                 END;

     END; { while }
END;


VAR SBP_FoundUsInPath : BOOLEAN;    { for circular PATH detection }
    SBP_FoundNotUsInPath : BOOLEAN; { prevent duping local AKAs only }

{--------------------------------------------------------------------------}
{ StoreSBP                                                                 }
{                                                                          }
{ This routine is called for each line in the footer. It searches for      }
{ SEEN-BY lines, which it removes and replaced with a single line to       }
{ indicate an error situation. The PATH line is buffered until a none-path }
{ lines comes by, after which it is updated with our origin node number    }
{ for this area and added to the new footer.                               }
{                                                                          }
FUNCTION StoreSBP (VAR Regel : STRING) : BOOLEAN; FAR;
BEGIN
     StoreSBP:=FALSE; { never abort }

     IF (Copy (Regel,1,8) = 'SEEN-BY:') THEN
     BEGIN
          IF (Msg.FirstSeenByPtr = NIL) THEN
             MsgsAddLineTo (Footer_F,'SEEN-BY: error');

          ScanAndStore (Regel,8,{OurNodeCheck:}FALSE,Msg.FirstSeenByPtr);

          Exit; { do not add to Footer_F }
     END;

     IF (Copy (Regel,1,6) = #1'PATH:') THEN
     BEGIN
          IF ScanAndStore (Regel,6,{OurNodeCheck:}TRUE,Msg.FirstPathPtr) THEN
             SBP_FoundUsInPath:=TRUE
          ELSE
              SBP_FoundNotUsInPath:=TRUE; { assumes no empty PATH lines }

          Exit; { do not add to Footer_F }
     END;

     { not a SEEN-BY or PATH regel - just add }
     MsgsAddLineToNoEOL (Footer_F,Regel);
END;


{--------------------------------------------------------------------------}
{ SBP_StoreSeenBysAndPath                                                  }
{                                                                          }
{ This routine is called when an echomail message has been read into the   }
{ internal storage structure. Here we scan the footer for SEEN-BY and PATH }
{ lines and store these in the internal message structure (this could be   }
{ moved to an earlier stage lager on).                                     }
{ If we find our own node number in the PATH line (circular path dupe      }
{ detection) then we return TRUE and abort this message.                   }
{                                                                          }
FUNCTION SBP_StoreSeenBysAndPath (FromZone : WORD) : BOOLEAN;

VAR OldFooter : TopRegelRecordPtr;

BEGIN
     SBP_FoundUsInPath:=FALSE;
     SBP_FoundNotUsInPath:=FALSE;

     IF (FromZone = 0) THEN
        LogMessage (liReport,'From Zone information is missing');

     Msg.SBP_Zone:=FromZone;

     IF (Msg.FirstSeenByPtr <> NIL) THEN
     BEGIN
          LogMessage (liReport,'Unexpected: Already a SEEN-BY administration present');
          SBP_ReleaseSBPBlocks (Msg.FirstSeenByPtr);
     END;

     IF (Msg.FirstPathPtr <> NIL) THEN
     BEGIN
          LogMessage (liReport,'Unexpected: Already a PATH administration present');
          SBP_ReleaseSBPBlocks (Msg.FirstPathPtr);
     END;

     OldFooter:=Msg.FooterTop_F;
     Msg.FooterTop_F:=NIL;
     MsgsForEachKill (OldFooter,StoreSBP);

     IF SBP_FoundUsInPath AND (NOT SBP_FoundNotUsInPath) THEN
     BEGIN
          SBP_FoundUsInPath:=FALSE;
          LogMessage (liTrivial,'Found just our AKA(s) in PATH');
          SBP_ReleaseSBPBlocks (Msg.FirstPathPtr);
     END;

     SBP_StoreSeenBysAndPath:=SBP_FoundUsInPath;
END;


{--------------------------------------------------------------------------}
{ SBP_StoreEmptySeenByAndPath                                              }
{                                                                          }
{ This routine is called after translating a news article to echomail. The }
{ message is at this point in no zone at all and there are no SEEN-BY or   }
{ PATH lines. We therefore just clear the internal administration.         }
{                                                                          }
PROCEDURE SBP_StoreEmptySeenByAndPath;
BEGIN
     IF (Msg.FirstSeenByPtr <> NIL) THEN
        SBP_ReleaseSBPBlocks (Msg.FirstSeenByPtr);

     IF (Msg.FirstPathPtr <> NIL) THEN
        SBP_ReleaseSBPBlocks (Msg.FirstPathPtr);

     Msg.SBP_Zone:=0; { no specific zone }
END;


VAR ReplaceSBP_First      : BOOLEAN;
    ReplaceSBP_UserList   : SubscriptBaseRecordNrType;
    ReplaceSBP_AddToPath  : BOOLEAN;
    ReplaceSBP_PathNet    : WORD;
    ReplaceSBP_PathNode   : WORD;
    ReplaceSBP_SeenByList : AkaBitList;

{--------------------------------------------------------------------------}
{ WriteNewSeenByBlock                                                      }
{                                                                          }
{ This routine writes a new SEEN-BY block to the footer of the message.    }
{ It starts with counting how many subscribers this area has. Next, the    }
{ number of net/node combinations already stored for this message instance }
{ are counted by browsing the chained list of block records. The sum is    }
{ the size of the array of net/node numbers that will be allocated         }
{ dynamically. This array is then filled with all the message net/node     }
{ numbers (a copy) and completed with the not yet present net/node numbers }
{ of the users that would get a copy.                                      }
{ If a user was already present, then its exported[] entry is marked so it }
{ won't get a copy.                                                        }
{                                                                          }
PROCEDURE WriteNewSeenByBlock;

CONST MAX_NETNODES = 16000;
      SEENBY_STR = 'SEEN-BY:';

TYPE NetNodePair = RECORD
                         Net,
                         Node : WORD;
                   END;

TYPE NetNodeArray = ARRAY[1..MAX_NETNODES] OF NetNodePair;

VAR NNPtr   : ^NetNodeArray;
    NNSize  : WORD;
    NNCount : WORD;

    PROCEDURE AddNN_NoDups (Net,Node : WORD);

    VAR Lp : WORD;

    BEGIN
         { search for duplicates }
         FOR Lp:=1 TO NNCount DO
             IF (NNPtr^[Lp].Net = Net) AND (NNPtr^[Lp].Node = Node) THEN
                Exit;

         { add the new net/node pair }
         Inc (NNCount);
         NNPtr^[NNCount].Net:=Net;
         NNPtr^[NNCount].Node:=Node;
    END;

VAR SBBPtr  : SBP_BlockPtr;
    Search  : SubscrSearchRecord;
    UserRec : UserBaseRecord;
    Net     : WORD;
    Node    : WORD;
    FoundIX : WORD;
    Lp      : WORD;
    Regel   : STRING;
    DoNet   : BOOLEAN;
    PairStr : STRING[12];

BEGIN
     { count how many people are subscribed to this area }
     NNSize:=CountSubscribedUsers (ReplaceSBP_UserList);

     { add the number of SEEN-BY akas that are marked for this area }
     FOR Lp:=1 TO MaxAKAs DO
         IF HasSeenByBit (ReplaceSBP_SeenByList,Lp) THEN
            IF (Config.NodeNrs[Lp].Point = 0) THEN
               Inc (NNSize);

     { add to this the number of net/node pairs in the administration }
     SBBPtr:=Msg.FirstSeenByPtr;
     WHILE (SBBPtr <> NIL) DO
     BEGIN
          Inc (NNSize,SBBPtr^.Count);
          SBBPtr:=SBBPtr^.NextBlockPtr;
     END; { while }

     IF (NNSize > MAX_NETNODES) THEN
     BEGIN
          LogMessage (liReport,'[ReplaceSB] Too many net/node pairs!');
          NNSize:=MAX_NETNODES;
          {## note: no checking is done below! }
     END;

     { allocate the array }
     GetMem (NNPtr,NNSize*SizeOf (NetNodePair));
     NNCount:=0;

     { copy all the message net/node pairs in here }
     SBBPtr:=Msg.FirstSeenByPtr;
     WHILE (SBBPtr <> NIL) DO
     BEGIN
          FOR Lp:=1 TO SBBPtr^.Count DO
              AddNN_NoDups (SBBPtr^.Nets[Lp],SBBPtr^.Nodes[Lp]);

          SBBPtr:=SBBPtr^.NextBlockPtr;
     END; { while }

     { add to that all the not yet present net/node pairs of the subscribers }
     GetFirstUserSubscribedToThisArea (ReplaceSBP_UserList,Search);
     WHILE (Search.Found) DO
     BEGIN
          IF (Msg.Exported^[Search.UserBaseRecordNr] = etReady) THEN
          BEGIN
               ReadUserBaseRecord (Search.UserBaseRecordNr,UserRec);

               { Add NODES to the list, not POINTS because their boss }
               { would never get the message!!                        }
               IF (UserRec.System = _F) AND (UserRec.Address.Point = 0) THEN
               BEGIN
                    Net:=UserRec.Address.Net;
                    Node:=UserRec.Address.Node;

                    FOR Lp:=1 TO NNCount DO
                        IF (NNPtr^[Lp].Net = Net) AND (NNPtr^[Lp].Node = Node) THEN
                        BEGIN
                             { existing net/node pair: mark user as exported }
                             Msg.Exported^[Search.UserBaseRecordNr]:=etInPathOrSB;
                             Break;
                        END;

                    IF (Msg.Exported^[Search.UserBaseRecordNr] = etReady) THEN
                       AddNN_NoDups (Net,Node);

               END; { _F }
          END; { etReady }

          GetNextUserSubscribedToThisArea (Search);
     END;

     { add all the net/node pairs marked in the Area Definition }
     FOR Lp:=1 TO MaxAKAs DO
         IF HasSeenByBit (ReplaceSBP_SeenByList,Lp) AND
            (Config.NodeNrs[Lp].Point = 0)
         THEN
             AddNN_NoDups (Config.NodeNrs[Lp].Net,Config.NodeNrs[Lp].Node);

     { all net/node pairs that have to be in the SEEN-BY must now be      }
     { written to the SEEN-BY lines at the current position in the footer }
     Regel:=SEENBY_STR;

     WHILE (NNCount > 0) DO
     BEGIN
          { zoek het laagste net nummer van allemaal }
          Net:=NNPtr^[1].Net;
          FOR Lp:=1 TO NNCount DO
              IF (NNPtr^[Lp].Net < Net) THEN
                 Net:=NNPtr^[Lp].Net;

          DoNet:=TRUE; { sticky net handling }

          { nu alle net/node pairs met dit net nummer toevoegen }
          REPEAT
                { zoek het laagste node number bij dit net }
                Node:=65535;
                FoundIX:=0; { non-existing }

                FOR Lp:=1 TO NNCount DO
                    IF (NNPtr^[Lp].Net = Net) THEN
                       IF (NNPtr^[Lp].Node <= Node) THEN
                       BEGIN
                            FoundIX:=Lp;
                            Node:=NNPtr^[Lp].Node; { new lowest }
                       END;

                IF (FoundIX <> 0) THEN
                BEGIN
                     { FoundIX wijst nu naar de index met het laagste }
                     { node nummer. Voeg die toe.                     }

                     PairStr:=' ';

                     IF DoNet THEN
                     BEGIN
                          { nieuw net nummer }
                          PairStr:=PairStr+Word2String (NNPtr^[FoundIX].Net)+'/';
                          DoNet:=FALSE;
                     END;

                     PairStr:=PairStr+Word2String (NNPtr^[FoundIX].Node);

                     IF (Length (Regel)+Length (PairStr) > 79) THEN
                     BEGIN
                          MsgsAddLineTo (Footer_F,Regel);
                          Regel:=SEENBY_STR+
                                 ' '+Word2String (NNPtr^[FoundIX].Net)+
                                 '/'+Word2String (NNPtr^[FoundIX].Node);
                          { DoNet is already FALSE }
                     END ELSE
                         Regel:=Regel+PairStr;

                     { FoundIX nu uit de lijst verwijderen }
                     NNPtr^[FoundIX]:=NNPtr^[NNCount];
                     Dec (NNCount);
                END;

          UNTIL (FoundIX = 0) OR (NNCount = 0);

     END; { while }

     IF (Regel <> SEENBY_STR) THEN
        MsgsAddLineTo (Footer_F,Regel);

     { free the net/node copy administration }
     FreeMem (NNPtr,NNSize*SizeOf (NetNodePair));
END;


{--------------------------------------------------------------------------}
{ WriteNewPathBlock                                                        }
{                                                                          }
{ This routine adds a new PATH to the message based on the stored PATH     }
{ information (from when the message was read from disk). We add our own   }
{ net/node pair at the end based on the Origin AKA set for this area.      }
{                                                                          }
PROCEDURE WriteNewPathBlock;

CONST PATH_STR = #1'PATH:';

VAR CurrNet : WORD;
    Regel   : STRING;

    PROCEDURE AddNN (Net,Node : WORD);

    VAR PairStr : STRING[12];

    BEGIN
         IF (CurrNet <> Net) THEN
            PairStr:=Word2String (Net)+'/'+Word2String (Node)
         ELSE
             PairStr:=Word2String (Node);

         IF (Length (Regel)+Length (PairStr) >= 78{space&cr}) THEN
         BEGIN
              MsgsAddLineTo (Footer_F,Regel);
              Regel:=PATH_STR;

              { need to specify full net and node }
              PairStr:=Word2String (Net)+'/'+Word2String (Node);
              CurrNet:=Net;
         END;

         Regel:=Regel+' '+PairStr;
    END;

{ WriteNewPathBlock }

VAR PBPtr : SBP_BlockPtr;
    Lp    : 1..NN_PAIRS_PER_BLOCK;

BEGIN
     Regel:=PATH_STR;
     CurrNet:=0; { impossible? }

     PBPtr:=Msg.FirstPathPtr;
     WHILE (PBPtr <> NIL) DO
     BEGIN
          FOR Lp:=1 TO PBPtr^.Count DO
              AddNN (PBPtr^.Nets[Lp],PBPtr^.Nodes[Lp]);

          PBPtr:=PBPtr^.NextBlockPtr;
     END; { while }

     { add the Origin AKA for this area at the end of the PATH line }
     IF ReplaceSBP_AddToPath THEN
        AddNN (ReplaceSBP_PathNet,ReplaceSBP_PathNode);

     { add last line }
     IF (Regel <> PATH_STR) THEN
        MsgsAddLineTo (Footer_F,Regel);
END;


{--------------------------------------------------------------------------}
{ ReplaceSBP                                                               }
{                                                                          }
{ This routine is called for each line in the footer. It removes the       }
{ SEEN-BY and PATH lines and adds a new block based on the area in which   }
{ the message will be distributed.                                         }
{                                                                          }
FUNCTION ReplaceSBP (VAR Regel : STRING) : BOOLEAN; FAR;
BEGIN
     ReplaceSBP:=FALSE; { do not abort }

     IF (Copy (Regel,1,8) = 'SEEN-BY:') THEN
     BEGIN
          { found a SEEN-BY line. Replace it with our block }
          IF ReplaceSBP_First THEN
          BEGIN
               WriteNewSeenByBlock;
               WriteNewPathBlock;

               ReplaceSBP_First:=FALSE;
          END;

          Exit; { remove line }
     END;

     IF (Copy (Regel,1,6) = #1'PATH:') THEN
        Exit; { remove it }

     MsgsAddLineToNoEOL (Footer_F,Regel);
END;


{--------------------------------------------------------------------------}
{ SBP_ReplaceSeenBysAndPath                                                }
{                                                                          }
{ This routine replaces the SEEN-BY block for the message being exported.  }
{ It removes any other SEEN-BY lines already in the footer and creates the }
{ block based on the SEEN-BYs stored when the message was read from disk   }
{ plus the net/node pairs for the subscribed users and the system net/node }
{ pairs marked in the area record for adding.                              }
{ The Exported[] flags for FTN users are also updated. Old etInPathOrSB    }
{ must be reset to etReady before calling this routine. Here set the flag  }
{ etInPathOrSB when the net/node pair is found in the SEEN-BY or PATH      }
{ administration.                                                          }
{                                                                          }
PROCEDURE SBP_ReplaceSeenBysAndPath (VAR AreaRec : AreaBaseRecord);

VAR OldFooter : TopRegelRecordPtr;
    FromZone  : WORD;

BEGIN
     { check zone }
     IF (Msg.SBP_Zone = 0) THEN
        {## correct??? 0=free zone?}
        FromZone:=Config.NodeNrs[AreaRec.OriginAKA].Zone
     ELSE
         IF (Msg.SBP_Zone <> Config.NodeNrs[AreaRec.OriginAKA].Zone) THEN
         BEGIN
              LogMessage (liConfig,'Config Error: Message in Zone '+Word2String (Msg.SBP_Zone)+
                          ' distributed in area in zone '+Word2String (Config.NodeNrs[AreaRec.OriginAKA].Zone));
              LogExtraMessage ('(Area '+AreaRec.AreaName_F+')');
         END;

     { pointnet handling?? }

     { Store net and node part of Origin AKA that has to be added at }
     { the end of the PATH.                                          }

     { If the Origin AKA is a point number, then don't add the net/node }
     { combination to the path.                                         }
     ReplaceSBP_AddToPath:=(Config.NodeNrs[AreaRec.OriginAKA].Point = 0);
     ReplaceSBP_PathNet:=Config.NodeNrs[AreaRec.OriginAKA].Net;
     ReplaceSBP_PathNode:=Config.NodeNrs[AreaRec.OriginAKA].Node;

     ReplaceSBP_First:=TRUE; { nothing replaced yet }
     ReplaceSBP_UserList:=AreaRec.UserList;

     ReplaceSBP_SeenByList:=AreaRec.AddSeenByAkas;

     OldFooter:=Msg.FooterTop_F;
     Msg.FooterTop_F:=NIL;
     MsgsForEachKill (OldFooter,ReplaceSBP);

     { if nothing was replaced, then simply add our block at the end }
     IF ReplaceSBP_First THEN
     BEGIN
          WriteNewSeenByBlock;
          WriteNewPathBlock;
     END;
END;


END.
