#include <CrashMail.h>

extern BOOL isbounce;

void SendRemoteAreafix(void);

void afInitReply(UBYTE *fromname,struct Node4D *from4d,UBYTE *toname,struct Node4D *to4d,UBYTE *subject);
void afAddLine(UBYTE *fmt,...);
void afSendMessage(void);

#define SENDLIST_FULL      1
#define SENDLIST_QUERY     2
#define SENDLIST_UNLINKED  3

void rawSendList(short type,struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode);
void rawSendHelp(struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode);
void rawSendInfo(struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode);
void rawRemoveArea(struct Area *area);

void convertstar(UBYTE *buf,ULONG len);

#define COMMAND_UPDATE     1
#define COMMAND_ADD        2
#define COMMAND_REMOVE     3

AreaFix(void)
{
   struct Arealist *arealist;
   struct ConfigNode *cnode;
   struct Area *area,*lastarea;
   struct TossNode   *temptnode,*lasttnode;
   struct BannedNode *bannednode;
   struct TextChunk *chunk;
   ULONG c,d,num,q;
   UBYTE password[100],buf[100],buf2[100];
   BOOL stop,sendareaquery,sendarealist,sendareaunlinked,sendhelp,sendinfo,done,iswild;
   BOOL globalrescan,wasfeed;
   UBYTE *opt,areaname[100],pattern[210],command;
   UBYTE subject[100],fromname[40];
   struct TextChunk *firstchunk;
   struct Node4D orignode;
   struct MemMessage mmbak;
   struct MemMessage mmbak2;
   struct Route *tmproute;

   /* Backup old MemMessage */

   CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));
   firstchunk=MemMessage.TextChunks.First;

   Copy4D(&orignode,&MemMessage.OrigNode);
   strcpy(subject,MemMessage.Subject);
   strcpy(fromname,MemMessage.From);

   /* Init reply structure */

   jbNewList(&MemMessage.TextChunks);

   for(tmproute=RouteList.First;tmproute;tmproute=tmproute->Next)
      if(Compare4DPat(&tmproute->Pattern,&orignode)==0) break;

   if(!tmproute)
   {
      LogWrite(1,TOSSINGERR,"No route found for %ld:%ld/%ld.%ld",
         orignode.Zone,
         orignode.Net,
         orignode.Node,
         orignode.Point);

      return;
   }

   afInitReply(cfg_Sysop,&tmproute->Aka->Node,fromname,&orignode,"AreaFix response");

   for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
      if(Compare4D(&cnode->Node,&orignode)==0) break;

   LogWrite(4,AREAFIX,"AreaFix: Request from %lu:%lu/%lu.%lu",orignode.Zone,
                                                              orignode.Net,
                                                              orignode.Node,
                                                              orignode.Point);

   if(!cnode)
   {
      LogWrite(2,AREAFIX,"AreaFix: Unknown node %lu:%lu/%lu.%lu",orignode.Zone,
                                                                 orignode.Net,
                                                                 orignode.Node,
                                                                 orignode.Point);


      afAddLine("Sorry, your node is not configured here.");
      afSendMessage();
      jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
      return;
   }

   jbcpos=0;
   jbstrcpy(password,subject,100);

   if(stricmp(password,cnode->AreafixPW)!=0)
   {
      LogWrite(2,AREAFIX,"AreaFix: Wrong password \"%s\" from %lu:%lu/%lu.%lu",password,
                                                                               orignode.Zone,
                                                                               orignode.Net,
                                                                               orignode.Node,
                                                                               orignode.Point);

      afAddLine("Sorry, wrong password.");
      afSendMessage();
      jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
      return;
   }

   sendarealist=FALSE;
   sendareaquery=FALSE;
   sendareaunlinked=FALSE;
   sendinfo=FALSE;
   sendhelp=FALSE;
   
   globalrescan=FALSE;

   done=FALSE;

   jbcpos=0;
   jbstrcpy(password,subject,100);

   if(jbstrcpy(password,subject,100))
   {
      if(stricmp(password,"-q")==0 || stricmp(password,"-l")==0)
      {
         done=TRUE;
         sendarealist=TRUE;
         afAddLine("%-30.30s Sending list of all areas",password);
      }
   }

   stop=FALSE;

   for(chunk=firstchunk;chunk && !stop && !nomem && !exitprg;chunk=chunk->Next)
   {
      for(c=0;c<chunk->Length && stop==FALSE;c++)
      {
         for(d=0;d<100 && chunk->Data[c+d]!=13 && chunk->Data[c+d]!=10 && c+d<chunk->Length;d++)
            buf[d]=chunk->Data[c+d];

         buf[d]=0;
         c+=d;

         if(strncmp(buf,"---",3)==0)
            stop=TRUE;

         else if(buf[0]=='%')
         {
            jbcpos=0;
            jbstrcpy(buf2,buf,100);

            if(stricmp(buf2,"%PAUSE")==0)
            {
               if(cnode->Flags & NODE_PASSIVE)
               {
                  afAddLine("%-30.30s Your system is already passive",buf);
               }
               else
               {
                  cnode->Flags|=NODE_PASSIVE;
                  saveconfig=TRUE;
                  done=TRUE;
                  afAddLine("%-30.30s Your system is marked as passive.",buf);
                  afAddLine("%-30.20s Send %%RESUME to get echomail again.","");
               }
            }
            else if(stricmp(buf2,"%RESUME")==0)
            {
               if(cnode->Flags & NODE_PASSIVE)
               {
                  cnode->Flags&=~NODE_PASSIVE;
                  saveconfig=TRUE;
                  done=TRUE;
                  afAddLine("%-30.30s Your system is active again.",buf);
               }
               else
               {
                  afAddLine("%-30.30s Your system is not paused.",buf);
               }
            }
            else if(stricmp(buf2,"%PWD")==0)
            {
               if(jbstrcpy(buf2,buf,40))
               {
                  strcpy(cnode->AreafixPW,buf2);
                  saveconfig=TRUE;
                  done=TRUE;
                  afAddLine("%-30.30s AreaFix password changed.",buf);
               }
               else
               {
                  afAddLine("%-30.30s No new password specified.",buf);
               }
            }
            else if(stricmp(buf2,"%RESCAN")==0)
            {
               if(cfg_Flags & CFG_ALLOWRESCAN)
               {
                  afAddLine("%-30.30s Will rescan all areas added after this line",buf);
                  globalrescan=TRUE;
                  RescanNode=cnode;
               }
               else
               {
                  afAddLine("%-30.30s No rescanning allowed",buf);
               }
            }
            else if(stricmp(buf2,"%COMPRESS")==0)
            {
               if(jbstrcpy(buf2,buf,40))
               {
                  BOOL gotpacker;
                  struct Packer *tmppacker;

                  gotpacker=FALSE;
                  tmppacker=NULL;

                  if(buf2[0]!='?')
                  {
                     if(stricmp(buf2,"NONE")==0)
                     {
                        tmppacker=NULL;
                        gotpacker=TRUE;
                     }
                     else
                     {
                        for(tmppacker=PackerList.First;tmppacker;tmppacker=tmppacker->Next)
                           if(stricmp(buf2,tmppacker->Name)==0 && tmppacker->Packer[0]) break;

                        if(tmppacker)
                           gotpacker=TRUE;

                        else
                           afAddLine("%-30.20s Unknown packer. Choose from this list:",buf);
                     }
                  }
                  else
                  {
                     afAddLine("%-30.30s Sending list of packers:",buf);
                  }

                  if(gotpacker)
                  {
                     cnode->Packer=tmppacker;
                     saveconfig=TRUE;
                     afAddLine("%-30.20s Packer changed",buf);
                  }
                  else
                  {
                     for(tmppacker=PackerList.First;tmppacker;tmppacker=tmppacker->Next)
                     {
                        if(tmppacker->Packer[0])
                           afAddLine("%-30.30s %s","",tmppacker->Name);
                     }

                     afAddLine("%-30.30s %s","","NONE");
                  }
                  done=TRUE;
               }
               else
               {
                  afAddLine("%-30.30s No new method specified.",buf);
               }
            }
            else if(stricmp(buf2,"%LIST")==0)
            {
               sendarealist=TRUE;
               done=TRUE;
               afAddLine("%-30.30s Sending list of all areas",buf);
            }

            else if(stricmp(buf2,"%QUERY")==0)
            {
               sendareaquery=TRUE;
               done=TRUE;
               afAddLine("%-30.30s Sending query",buf);
            }

            else if(stricmp(buf2,"%UNLINKED")==0)
            {
               sendareaunlinked=TRUE;
               done=TRUE;
               afAddLine("%-30.30s Sending list of all unlinked areas",buf);
            }
            else if(stricmp(buf2,"%HELP")==0)
            {
               sendhelp=TRUE;
               done=TRUE;
               afAddLine("%-30.30s Sending help file",buf);
            }
            else if(stricmp(buf2,"%INFO")==0)
            {
               sendinfo=TRUE;
               done=TRUE;
               afAddLine("%-30.30s Sending configuration info",buf);
            }
            else
            {
               done=TRUE;
               afAddLine("%-30.30s Unknown command",buf);
            }
         }
         else if(buf[0]!=1 && buf[0]!=0)
         {
            ULONG rescannum;
            BOOL patterndone,dorescan,areaexists;
            struct Area *rescanarea;

            done=TRUE;
            rescannum=0;

            dorescan=FALSE;

            /* Separate command, name and opt */

            mystrncpy(areaname,buf,100);
            convertstar(areaname,100);

            opt="";

            for(q=0;areaname[q];q++)
               if(areaname[q]==',') opt=&areaname[q];

            if(opt[0]==',')
            {
               opt[0]=0;
               opt=&opt[1];

               while(opt[0]==32)
                  opt=&opt[1];
            }

            strip(areaname);
            strip(opt);

            if(areaname[0]=='-')
            {
               command=COMMAND_REMOVE;
               strcpy(areaname,&areaname[1]);
            }
            else if(areaname[0]=='=')
            {
               command=COMMAND_UPDATE;
               strcpy(areaname,&areaname[1]);
            }
            else
            {
               command=COMMAND_ADD;

               if(areaname[0]=='+')
                  strcpy(areaname,&areaname[1]);
            }

            iswild=ParsePatternNoCase(areaname,pattern,104);

            if(iswild == -1)
            {
               afAddLine("%-30.30s Invalid pattern",buf);
            }
            else
            {
               if(iswild)
               {
                  afAddLine("%s",buf);
                  afAddLine("");
               }

               patterndone=FALSE;
               areaexists=FALSE;
               rescanarea=NULL;

               for(area=AreaList.First;area;area=area->Next)
                  if(!(area->Flags & AREA_BAD) && !(area->Flags & AREA_NETMAIL) && !(area->Flags & AREA_DEFAULT))
                  {
                     if(MatchPatternNoCase(pattern,area->Tagname))
                     {
                        areaexists=TRUE;

                        for(temptnode=area->TossNodes.First;temptnode;temptnode=temptnode->Next)
                           if(temptnode->ConfigNode == cnode) break;

                        switch(command)
                        {
                           case COMMAND_ADD:
                              if(!temptnode)
                              {
                                 /* Do we have access? */

                                 if(CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups))
                                 {
                                    patterndone=TRUE;

                                    for(bannednode=area->BannedNodes.First;bannednode;bannednode=bannednode->Next)
                                       if(bannednode->ConfigNode == cnode) break;

                                    /* Are we banned? */

                                    if(bannednode)
                                    {
                                       if(iswild)
                                          afAddLine("   You have been banned from %s",area->Tagname);

                                       else
                                          afAddLine("%-30.30s You have been banned from that area",buf);

                                       LogWrite(3,AREAFIX,"AreaFix: This node is banned in %s",area->Tagname);
                                    }
                                    else
                                    {
                                       if(area->TossNodes.First && area->TossNodes.First->Next && Checksum9()!=checksums[9])
                                       {
                                          LogWrite(1,AREAFIX,"AreaFix: Failed to attach to %s",area->Tagname);
                                          LogWrite(1,AREAFIX,"AreaFix: You can only export an area to two nodes in the unregistered version");

                                          if(iswild)
                                             afAddLine("   No more nodes allowed when unregistered");

                                          else
                                             afAddLine("%-30.30s No more nodes allowed when unregistered",buf);
                                       }
                                       else
                                       {
                                          if((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group,cnode->ReadOnlyGroups))
                                          {
                                             LogWrite(4,AREAFIX,"AreaFix: Attached to %s as read-only",area->Tagname);

                                             if(iswild)
                                                afAddLine("   Attached to %s as read-only",area->Tagname);

                                             else
                                                afAddLine("%-30.30s Attached as read-only",buf);
                                          }
                                          else
                                          {
                                             LogWrite(4,AREAFIX,"AreaFix: Attached to %s",area->Tagname);

                                             if(iswild)
                                                afAddLine("   Attached to %s",area->Tagname);

                                             else
                                                afAddLine("%-30.30s Attached",buf);
                                          }

                                          if(!(temptnode=AllocMem(sizeof(struct TossNode),MEMF_CLEAR)))
                                          {
                                             jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
                                             CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
                                             nomem=TRUE;
                                             return;
                                          }

                                          temptnode->ConfigNode=cnode;

                                          if((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group,cnode->ReadOnlyGroups))
                                             temptnode->Flags=TOSSNODE_READONLY;

                                          jbAddNode(&area->TossNodes,(struct jbNode *)temptnode);
                                          rescanarea=area;
                                          saveconfig=TRUE;
                                       }
                                    }
                                 }
                                 else if(!iswild)
                                 {
                                    afAddLine("%-30.30s You don't have access to that area",buf);
                                 }
                              }
                              else
                              {
                                 patterndone=TRUE;

                                 if(iswild)
                                    afAddLine("   You are already attached to %s",area->Tagname);

                                 else
                                    afAddLine("%-30.30s You are already attached to that area",buf);
                              }
                              break;

                           case COMMAND_REMOVE:
                              if(!temptnode)
                              {
                                 if(!iswild)
                                 {
                                    afAddLine("%-30.30s You are not attached to that area",buf);
                                    patterndone=TRUE;
                                 }
                              }
                              else
                              {
                                 patterndone=TRUE;

                                 if((area->Flags & AREA_MANDATORY) && !(temptnode->Flags & TOSSNODE_FEED))
                                 {
                                    if(iswild)
                                       afAddLine("   You are not allowed to detach from %s",area->Tagname);

                                    else
                                       afAddLine("%-30.30s You are not allowed to detach from that area",buf);
                                 }
                                 else
                                 {
                                    LogWrite(4,AREAFIX,"AreaFix: Detached from %s",area->Tagname);

                                    if(iswild)
                                       afAddLine("   Detached from %s",area->Tagname);

                                    else
                                       afAddLine("%-30.30s Detached",buf);

                                    for(lasttnode=area->TossNodes.First;lasttnode;lasttnode=lasttnode->Next)
                                       if(lasttnode->Next == temptnode) break;

                                    if(lasttnode)
                                       lasttnode->Next=temptnode->Next;

                                    else
                                       area->TossNodes.First=temptnode->Next;

                                    if(area->TossNodes.Last == (struct jbNode *)temptnode)
                                       area->TossNodes.Last = lasttnode;

                                    wasfeed=FALSE;

                                    if(temptnode->Flags & TOSSNODE_FEED)
                                       wasfeed=TRUE;

                                    FreeMem(temptnode,sizeof(struct TossNode));
                                    saveconfig=TRUE;

                                    if(wasfeed && (cfg_Flags & CFG_REMOVEWHENFEED))
                                    {
                                       LogWrite(2,AREAFIX,"AreaFix: Feed disconnected, removing area %s",area->Tagname);

                                       CopyMem(&MemMessage,&mmbak2,sizeof(struct MemMessage));
                                       rawRemoveArea(area);
                                       CopyMem(&mmbak2,&MemMessage,sizeof(struct MemMessage));
                                    }
                                    else if(cfg_Flags & CFG_AREAFIXREMOVE)
                                    {
                                       if(area->TossNodes.First == NULL ||
                                         (((struct TossNode*)area->TossNodes.First)->Next==NULL &&
                                          ((struct TossNode*)area->TossNodes.First)->Flags & TOSSNODE_FEED))
                                       {
                                          if(area->AreaType==0)
                                          {
                                             if(area->TossNodes.First)
                                             {
                                                LogWrite(3,AREAFIX,"AreaFix: Area %s removed, message sent to areafix",area->Tagname);
                                                sprintf(buf,"-%s",area->Tagname);
                                                RemoteAreafix(buf,((struct TossNode*)area->TossNodes.First)->ConfigNode);
                                             }
                                             else
                                             {
                                                LogWrite(3,AREAFIX,"AreaFix: Area %s removed",area->Tagname);
                                             }

                                             for(lastarea=AreaList.First;lastarea;lastarea=lastarea->Next)
                                                if(lastarea->Next == area) break;

                                             if(lastarea)
                                                lastarea->Next=area->Next;

                                             else
                                                AreaList.First=area->Next;

                                             if(AreaList.Last == (struct jbNode *)area)
                                                AreaList.Last = lastarea;

                                             FreeMem(area,sizeof(struct Area));

                                             area=lastarea;
                                          }
                                       }
                                    }
                                 }
                              }
                              break;

                           case COMMAND_UPDATE:
                              if(temptnode)
                              {
                                 patterndone=TRUE;

                                 if(iswild)
                                 {
                                    afAddLine("   Nothing to do with %s",area->Tagname);
                                 }
                                 else
                                 {
                                    afAddLine("%-30.30s Will rescan area",buf);
                                    rescanarea=area;
                                 }
                              }
                              break;
                        }
                     }
                  }


               if(command==COMMAND_UPDATE || command==COMMAND_ADD)
               {
                  if(!iswild && patterndone && rescanarea)
                  {
                     if(strnicmp(opt,"r=",2)==0)
                     {
                        rescannum=atoi(&opt[2]);
                        dorescan=TRUE;
                     }
                     else if(opt[0])
                     {
                        afAddLine("%-30.30s Unknown option %s","",opt);
                     }

                     if(globalrescan || dorescan)
                     {
                        if(cfg_Flags & CFG_ALLOWRESCAN)
                        {
                           if(rescanarea->AreaType == AREATYPE_UMS || rescanarea->AreaType == AREATYPE_MSG)
                           {
                              LogWrite(4,AREAFIX,"AreaFix: Rescanning %s",rescanarea->Tagname);
                              RescanNode=cnode;
                              num=Rescan(rescanarea,rescannum);
                              RescanNode=NULL;
                              LogWrite(4,AREAFIX,"AreaFix: Rescanned %lu messages",num);
                              afAddLine("%-30.30s Rescanned %lu messages","",num);
                           }
                           else
                           {
                              afAddLine("%-30.30s Can't rescan, area is pass-through","");
                           }
                        }
                        else
                        {
                           afAddLine("%-30.30s No rescanning allowed","");
                        }
                     }
                  }
               }

               switch(command)
               {
                  case COMMAND_ADD:
                     if(!patterndone)
                     {
                        if(iswild)
                        {
                           afAddLine("   There were no matching areas to connect to");
                        }
                        else
                        {
                           if(!areaexists)
                           {
                              if(cnode->Flags & NODE_FORWARDREQ)
                              {
                                 arealist=FindForward(areaname,cnode->Groups);

                                 if(arealist)
                                 {
                                    LogWrite(3,AREAFIX,"AreaFix: %s requested from %lu:%lu/%lu.%lu",areaname,arealist->Node->Node.Zone,arealist->Node->Node.Net,arealist->Node->Node.Node,arealist->Node->Node.Point);

                                    afAddLine("%-30.30s Request sent to %lu:%lu/%lu.%lu",buf,arealist->Node->Node.Zone,arealist->Node->Node.Net,arealist->Node->Node.Node,arealist->Node->Node.Point);
                                    RemoteAreafix(areaname,arealist->Node);

                                    area=AddArea(areaname,&arealist->Node->Node,&tmproute->Aka->Node,TRUE,cfg_Flags & CFG_FORWARDPASSTHRU);
                                    area->Group=arealist->Group;

                                    if(area)
                                    {
                                       UWORD flags;

                                       flags=0;

                                       if(CheckFlags(area->Group,cnode->ReadOnlyGroups))
                                          flags|=TOSSNODE_READONLY;

                                       AddTossNode(area,cnode,flags);

                                       saveconfig=TRUE;
                                       areaexists=TRUE;
                                    }
                                 }
                              }
                           }

                           if(!areaexists)
                           {
                              afAddLine("%-30.30s Unknown area",buf);
                              LogWrite(3,AREAFIX,"AreaFix: Unknown area %s",areaname);
                           }
                        }
                     }
                     break;

                  case COMMAND_REMOVE:
                     if(!patterndone)
                     {
                        if(iswild)
                           afAddLine("   There were no matching areas to detach from");

                        else
                           afAddLine("%-30.30s Unknown area",buf);
                     }
                     break;

                  case COMMAND_UPDATE:
                     if(!patterndone)
                     {
                        if(iswild)
                           afAddLine("   There were no matching areas");

                        else
                           afAddLine("%-30.30s You are not attached to this area",buf);
                     }
                     else
                     {
                        if(rescanarea && !globalrescan && opt[0]==0)
                           afAddLine("%-30.30s Nothing to do",buf);
                     }
                     break;
               }

               if(iswild)
                  afAddLine("");
            }
         }
      }
   }

   if(done==FALSE)
      afAddLine("Nothing to do.");

   if(exitprg || nomem)
   {
      jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      return;
   }

   afSendMessage();
   jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));

   if(sendarealist)
      rawSendList(SENDLIST_FULL,&tmproute->Aka->Node,fromname,cnode);

   if(sendareaquery)
      rawSendList(SENDLIST_QUERY,&tmproute->Aka->Node,fromname,cnode);

   if(sendareaunlinked)
      rawSendList(SENDLIST_UNLINKED,&tmproute->Aka->Node,fromname,cnode);

   if(sendhelp)
      rawSendHelp(&tmproute->Aka->Node,fromname,cnode);

   if(sendinfo)
      rawSendInfo(&tmproute->Aka->Node,fromname,cnode);

   /* Restore old MemMessage */

   CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));

   SendRemoteAreafix();
}

void rawRemoveArea(struct Area *area)
{
   struct Area *lastarea;
   struct TossNode *tn;
   struct DateStamp ds;
   UBYTE buf[100];

   for(tn=area->TossNodes.First;tn;tn=tn->Next)
   {
      if(tn->ConfigNode->Flags & NODE_SENDAREAFIX)
      {
         LogWrite(5,AREAFIX,"AreaFix: Sending message to AreaFix at %ld:%ld/%ld.%ld",
            tn->ConfigNode->Node.Zone,
            tn->ConfigNode->Node.Net,
            tn->ConfigNode->Node.Node,
            tn->ConfigNode->Node.Point);

         Copy4D(&MemMessage.DestNode,&tn->ConfigNode->Node);
         Copy4D(&MemMessage.OrigNode,&area->Aka->Node);

         strcpy(MemMessage.From,cfg_Sysop);
         strcpy(MemMessage.To,tn->ConfigNode->RemoteAFName);
         strcpy(MemMessage.Subject,tn->ConfigNode->RemoteAFPw);

         DateStamp(&ds);
         MakeFidoDate(&ds,MemMessage.DateTime);

         MemMessage.Attr = PVT;
         MemMessage.Cost = 0;

         MemMessage.BadReason[0]=0;
         MemMessage.MSGID[0]=0;
         MemMessage.REPLY[0]=0;

         jbNewList(&MemMessage.TextChunks);

         MakeNetmailKludges(&MemMessage.TextChunks,&MemMessage.OrigNode,&MemMessage.DestNode,cfg_Flags & CFG_ADDTID);

         sprintf(buf,"-%s\x0d",area->Tagname);
         AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

         sprintf(buf,"---\x0dGenerated by CrashMail "VERSION"\x0d");
         AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

         isbounce=TRUE;
         HandleNetmail();
         isbounce=FALSE;

         jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      }

      if((tn->ConfigNode->Flags & NODE_SENDTEXT) && !(tn->Flags & TOSSNODE_FEED))
      {
         LogWrite(5,AREAFIX,"AreaFix: Notifying sysop at %ld:%ld/%ld.%ld",
            tn->ConfigNode->Node.Zone,
            tn->ConfigNode->Node.Net,
            tn->ConfigNode->Node.Node,
            tn->ConfigNode->Node.Point);

         Copy4D(&MemMessage.DestNode,&tn->ConfigNode->Node);
         Copy4D(&MemMessage.OrigNode,&area->Aka->Node);

         strcpy(MemMessage.From,cfg_Sysop);
         strcpy(MemMessage.To,tn->ConfigNode->SysopName);
         strcpy(MemMessage.Subject,"Area removed");

         DateStamp(&ds);
         MakeFidoDate(&ds,MemMessage.DateTime);

         MemMessage.Attr = PVT;
         MemMessage.Cost = 0;

         MemMessage.BadReason[0]=0;
         MemMessage.MSGID[0]=0;
         MemMessage.REPLY[0]=0;

         jbNewList(&MemMessage.TextChunks);

         MakeNetmailKludges(&MemMessage.TextChunks,&MemMessage.OrigNode,&MemMessage.DestNode,cfg_Flags & CFG_ADDTID);

         sprintf(buf,"The area \"%s\" has been removed by the uplink.\x0d",area->Tagname);
         AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

         if(tn->ConfigNode->Flags & NODE_SENDAREAFIX)
         {
            sprintf(buf,"A message has been sent to your AreaFix.\x0d");
            AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));
         }

         isbounce=TRUE;
         HandleNetmail();
         isbounce=FALSE;

         jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      }
   }

   /* Remove from config */

   for(lastarea=AreaList.First;lastarea;lastarea=lastarea->Next)
      if(lastarea->Next == area) break;

   if(lastarea)
     lastarea->Next=area->Next;

   else
      AreaList.First=area->Next;

   if(AreaList.Last == (struct jbNode *)area)
      AreaList.Last = lastarea;

   FreeMem(area,sizeof(struct Area));

   /* Save config */

   saveconfig=TRUE;
}

struct SortStat
{
   struct SortStat *Next;
   UBYTE *Tagname;
   UBYTE *Desc;
   UBYTE Group;
   BOOL Attached;
   BOOL Feed;
   LONG WeekKB; /* -1 means unknown */
};

struct jbList SortList;
void *sortlistpool;

BOOL InitSortList(void)
{
	if(!(sortlistpool=LibCreatePool(MEMF_ANY,10000,500)))
	   return(FALSE);

   jbNewList(&SortList);

   return(TRUE);
}

void FreeSortList(void)
{
   LibDeletePool(sortlistpool);
}

BOOL AddSortList(UBYTE *tagname,UBYTE *desc,UBYTE group,BOOL attached,BOOL feed,LONG weekkb)
{
   UBYTE *pooltagname,*pooldesc;
   struct SortStat *ss;

   pooltagname=LibAllocPooled(sortlistpool,strlen(tagname)+1);
   pooldesc=LibAllocPooled(sortlistpool,strlen(desc)+1);
   ss=LibAllocPooled(sortlistpool,sizeof(struct SortStat));

   if(!pooltagname || !pooldesc || !ss)
      return(FALSE);

   strcpy(pooltagname,tagname);
   strcpy(pooldesc,desc);

   ss->Tagname=pooltagname;
   ss->Desc=pooldesc;
   ss->Group=group;
   ss->Attached=attached;
   ss->Feed=feed;
   ss->WeekKB=weekkb;

   jbAddNode(&SortList,(struct jbNode *)ss);

   return(TRUE);

}

int CompareAlpha(struct SortStat **s1, struct SortStat **s2)
{
   if((*s1)->Group < (*s2)->Group) return(-1);
   if((*s1)->Group > (*s2)->Group) return(1);

   return(stricmp((*s1)->Tagname,(*s2)->Tagname));
}

void SortSortList(void)
{
   ULONG nc,alloc;
   struct SortStat *ss,*buf,**work;

   nc=0;

   for(ss=SortList.First;ss;ss=ss->Next)
      nc++;

   if(nc==0)
      return;

   alloc=nc*4;

   if(!(buf=(struct SortStat **)AllocMem(alloc,MEMF_ANY)))
   {
      nomem=TRUE;
      return;
   }

   work=buf;

   for(ss=SortList.First;ss;ss=ss->Next)
      *work++=ss;

   qsort(buf,nc,4,CompareAlpha);

   jbNewList(&SortList);

   for(work=buf;nc--;)
      jbAddNode(&SortList,(struct jbNode *)*work++);

   FreeMem(buf,alloc);
}

LONG CalculateWeekKB(struct Area *area)
{
   if(area->FirstTime.ds_Days == 0 || DayStatsWritten == 0)
   {
      return(-1);
   }
   else
   {
      int days,c;
      unsigned long sum;

      days=DayStatsWritten-area->FirstTime.ds_Days;
      if(days > 7) days=7;

      sum=0;

      for(c=1;c<days+1;c++)
         sum+=area->Last8Days[c];

      if(sum == 0 && area->Texts!=0)
      {
         days=DayStatsWritten-area->FirstTime.ds_Days;
         if(days==0) days=1;
         return(area->Texts/days);
      }
      else
      {
         if(days == 0) days=1;
         return(sum/days);
      }
   }
}

BOOL AddForwardList(struct Arealist *arealist)
{
   BOOL res;
   BPTR fh;
   UBYTE buf[200];
   UBYTE desc[100];
   ULONG c,d;
   struct Area *area;
   struct SortStat *ss;
   
   if(!(fh=Open(arealist->AreaFile,MODE_OLDFILE)))
   {
      LogWrite(1,AREAFIX,"AreaFix: File %s not found",arealist->AreaFile);
      return(TRUE);
   }

   while(FGets(fh,buf,199))
   {
      desc[0]=0;

      for(c=0;buf[c]>32;c++);

      if(buf[c]!=0)
      {
         buf[c]=0;

         c++;
         while(buf[c]<=32 && buf[c]!=0) c++;

         if(buf[c]!=0)
         {
            d=0;
            while(buf[c]!=0 && buf[c]!=10 && buf[c]!=13 && d<77) desc[d++]=buf[c++];
            desc[d]=0;
         }
      }

      if(buf[0]!=0)
      {
         /* Don't add areas that exist locally */

         for(area=AreaList.First;area;area=area->Next)
            if(stricmp(buf,area->Tagname)==0) break;

         for(ss=SortList.First;ss;ss=ss->Next)
            if(stricmp(buf,ss->Tagname)==0) break;

         if(!area && !ss)
         {
            if(arealist->Flags & AREALIST_DESC)
               res=AddSortList(buf,desc,arealist->Group,FALSE,FALSE,-1);

            else
               res=AddSortList(buf,"",arealist->Group,FALSE,FALSE,-1);

            if(!res)
            {
               Close(fh);
               return(FALSE);
            }
         }
      }
   }
   
   Close(fh);
   return(TRUE);
}

void rawSendList(short type,struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode)
{
   UBYTE buf[50];
   struct TossNode *tn;
   struct Area *area;
   struct SortStat *ss,*lastss;
   struct Arealist *arealist;
   short sendlisttotal,sendlistlinked;
   UBYTE ast;

   /* Log action */

   switch(type)
   {
      case SENDLIST_QUERY:
         LogWrite(4,AREAFIX,"AreaFix: Sending query to %lu:%lu/%lu.%lu",
            cnode->Node.Zone,
            cnode->Node.Net,
            cnode->Node.Node,
            cnode->Node.Point);

         break;

      case SENDLIST_UNLINKED:
         LogWrite(4,AREAFIX,"AreaFix: Sending list of unlinked areas to %lu:%lu/%lu.%lu",
            cnode->Node.Zone,
            cnode->Node.Net,
            cnode->Node.Node,
            cnode->Node.Point);

         break;

      case SENDLIST_FULL:
         LogWrite(4,AREAFIX,"AreaFix: Sending list of areas to %lu:%lu/%lu.%lu",
            cnode->Node.Zone,
            cnode->Node.Net,
            cnode->Node.Node,
            cnode->Node.Point);

         break;
   }

   /* Start building reply message */

   afInitReply(cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix list of areas");

   switch(type)
   {
      case SENDLIST_QUERY:
         afAddLine("This is a list of all connected areas at %lu:%lu/%lu.%lu:",
            from4d->Zone,
            from4d->Net,
            from4d->Node,
            from4d->Point);
         break;

      case SENDLIST_FULL:
         afAddLine("This is a list of all available areas at %lu:%lu/%lu.%lu:",
            from4d->Zone,
            from4d->Net,
            from4d->Node,
            from4d->Point);
         break;

      case SENDLIST_UNLINKED:
         afAddLine("This is a list of all unlinked areas at %lu:%lu/%lu.%lu:",
            from4d->Zone,
            from4d->Net,
            from4d->Node,
            from4d->Point);
         break;
   }

   afAddLine("");

   /* Init list */

   if(!InitSortList())
   {
      LogWrite(1,SYSTEMERR,"AreaFix: Out of memory when building list of areas");
      afAddLine("Failed to build list of areas, out of memory");
      afSendMessage();
      jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
      FreeSortList();
      return;
   }

   /* Add local areas */

   for(area=(struct Area *)AreaList.First;area;area=area->Next)
      if(!(area->Flags & AREA_NETMAIL) && !(area->Flags & AREA_BAD) && !(area->Flags & AREA_DEFAULT))
      {
         short add;
         BOOL attached,feed;
         
         for(tn=(struct TossNode *)area->TossNodes.First;tn;tn=tn->Next)
            if(tn->ConfigNode == cnode) break;

         add=FALSE;

         switch(type)
         {
            case SENDLIST_QUERY:
               if(tn) add=TRUE;
               break;

            case SENDLIST_UNLINKED:
               if(!tn && (CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups))) add=TRUE;
               break;

            case SENDLIST_FULL:
               if(tn || (CheckFlags(area->Group,cnode->Groups) || CheckFlags(area->Group,cnode->ReadOnlyGroups))) add=TRUE;
               break;
         }

         if(add)
         {
            attached=FALSE;
            feed=FALSE;
            
            if(tn) attached=TRUE;
            if(tn && tn->Flags & TOSSNODE_FEED) feed=TRUE;

            if(!AddSortList(area->Tagname,area->Description,area->Group,attached,feed,CalculateWeekKB(area)))
            {
               LogWrite(1,SYSTEMERR,"AreaFix: Out of memory when building list of areas");
               afAddLine("Failed to build list of areas, out of memory");
               afSendMessage();
               jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
               FreeSortList();
               return;
            }
         }
      }

   /* Add forward-requestable area */

   if(cfg_Flags2 & CFG2_INCLUDEFORWARD && (type == SENDLIST_UNLINKED || type == SENDLIST_FULL) && cnode->Flags & NODE_FORWARDREQ)
   {
      for(arealist=ArealistList.First;arealist;arealist=arealist->Next)
         if((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group,cnode->Groups))
         {
            if(!AddForwardList(arealist))
            {
               LogWrite(1,SYSTEMERR,"AreaFix: Out of memory when building list of areas");
               afAddLine("Failed to build list of areas, out of memory");
               afSendMessage();
               jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
               FreeSortList();
               return;
            }
         }
   }

   /* Generate list */

   SortSortList();

   lastss=NULL;

   sendlisttotal=0;
   sendlistlinked=0;

   for(ss=(struct SortStat *)SortList.First;ss;ss=ss->Next)
   {
      if(!lastss || lastss->Group!=ss->Group)
      {
         if(lastss)
            afAddLine("");

         if(ss->Group) afAddLine(" Group: %s",cfg_GroupNames[ss->Group-'A']);
         else          afAddLine(" Group: %s","<No group>");

         afAddLine("");

         afAddLine(" Tagname                       Description                         KB/week");
         afAddLine(" ----------------------------  ---------------------------------   -------");
      }

      ast=' ';

      if(type == SENDLIST_FULL && ss->Attached)
      {
         ast='*';
         sendlistlinked++;
      }

      if(ss->Feed)
         ast='%';

      sendlisttotal++;

      if(ss->WeekKB == -1)
         strcpy(buf,"?");

      else
         sprintf(buf,"%ld",ss->WeekKB);

      if(strlen(ss->Tagname)<=28)
      {
         afAddLine("%lc%-28.28s  %-33.33s  %8.8s",ast,ss->Tagname,ss->Desc,buf);
      }
      else
      {
         afAddLine("%lc%-70.70s",ast,ss->Tagname);
         afAddLine("%lc%-28.28s  %-33.33s  %8.8s",' ',"",ss->Desc,buf);
      }
      lastss=ss;
   }

   switch(type)
   {
      case SENDLIST_QUERY:
         afAddLine("\x0d%lu linked areas.",sendlisttotal);
         afAddLine("A '%%' means that you are the feed for the area.");
         break;

      case SENDLIST_UNLINKED:
         afAddLine("\x0d%lu unlinked areas.",sendlisttotal);
         break;

      case SENDLIST_FULL:
         afAddLine("\x0dTotally %lu areas, you are connected to %lu of them.",sendlisttotal,sendlistlinked);
         afAddLine("A '*' means that you are connected to the area.");
         afAddLine("A '%%' means that you are the feed for the area.");
         break;
   }

   FreeSortList();
   afSendMessage();
   jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
}

void rawSendHelp(struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode)
{
   UBYTE helpbuf[100];
   BPTR fh;

   LogWrite(4,AREAFIX,"AreaFix: Sending help file to %lu:%lu/%lu.%lu",
      cnode->Node.Zone,
      cnode->Node.Net,
      cnode->Node.Node,
      cnode->Node.Point);

   afInitReply(cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix help");

   if(!(fh=Open(cfg_AreaFixHelp,MODE_OLDFILE)))
   {
      LogWrite(1,SYSTEMERR,"AreaFix: Unable to open %s",cfg_AreaFixHelp);
      afAddLine("*** Error *** : Couldn't open help file");
   }
   else
   {
      while(FGets(fh,helpbuf,100) && !nomem && !exitprg)
      {
         if(helpbuf[0]!=0)
            helpbuf[strlen(helpbuf)-1]=0;

         afAddLine("%s",helpbuf);
      }

      Close(fh);
   }

   afSendMessage();
   jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
}

void rawSendInfo(struct Node4D *from4d,UBYTE *toname,struct ConfigNode *cnode)
{
   int c;

   LogWrite(4,AREAFIX,"AreaFix: Sending configuration info to %lu:%lu/%lu.%lu",
      cnode->Node.Zone,
      cnode->Node.Net,
      cnode->Node.Node,
      cnode->Node.Point);

   afInitReply(cfg_Sysop,from4d,toname,&cnode->Node,"AreaFix configuration info");

   afAddLine("Configuration for %lu:%lu/%lu.%lu:",
      cnode->Node.Zone,
      cnode->Node.Net,
      cnode->Node.Node,
      cnode->Node.Point);

   afAddLine("");

   afAddLine("           Sysop: %s",cnode->SysopName);
   afAddLine("Packet  password: %s",cnode->PacketPW);
   afAddLine("Areafix password: %s",cnode->AreafixPW);

   if(!cnode->Packer)
   {
      afAddLine("          Packer: No packer");
   }
   else
   {
      afAddLine("          Packer: %s",cnode->Packer->Name);
   }

   afAddLine("");

   if(cnode->Flags & NODE_STONEAGE)
      afAddLine(" * You receive packets in StoneAge format (FTS-1 with no extensions)");

   if(cnode->Flags & NODE_PASSIVE)
      afAddLine(" * You are passive and will not receive any echomail messages");

   if(cnode->Flags & NODE_TINYSEENBY)
      afAddLine(" * You receive messages with tiny SEEN-BY lines");

   if(cnode->Flags & NODE_NOSEENBY)
      afAddLine(" * You receive messages without SEEN-BY lines");

   if(cnode->Flags & NODE_FORWARDREQ)
      afAddLine(" * You may do forward-requests");

   if(cnode->Flags & NODE_NOTIFY)
      afAddLine(" * You will receive notifications");

   if(cnode->Flags & NODE_PACKNETMAIL)
      afAddLine(" * Netmail to you will be packed");

   if(cnode->Flags & NODE_AUTOADD)
      afAddLine(" * New areas from you will be auto-added");

   afAddLine("");
   afAddLine("You have full access to these groups:");
   afAddLine("");

   for(c='A';c<='Z';c++)
      if(CheckFlags(c,cnode->Groups) && !CheckFlags(c,cnode->ReadOnlyGroups))
      {
         if(cfg_GroupNames[c-'A'][0]!=0)
            afAddLine("%lc: %s",c,cfg_GroupNames[c-'A']);

         else
            afAddLine("%lc",c);
      }

   afAddLine("");
   afAddLine("You have read-only access to these groups:");
   afAddLine("");

   for(c='A';c<='Z';c++)
      if(CheckFlags(c,cnode->ReadOnlyGroups))
      {
         if(cfg_GroupNames[c-'A'])
            afAddLine("%lc: %s",c,cfg_GroupNames[c-'A']);

         else
            afAddLine("%lc",c);
      }

   afSendMessage();
   jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));
}

UBYTE afFromName[40];
UBYTE afToName[40];
UBYTE afSubject[80];
struct Node4D afTo4D;
struct Node4D afFrom4D;
ULONG afLines;
ULONG afPart;

void afRawPrepareMessage(void);

void afInitReply(UBYTE *fromname,struct Node4D *from4d,UBYTE *toname,struct Node4D *to4d,UBYTE *subject)
{
   strcpy(afFromName,fromname);
   Copy4D(&afFrom4D,from4d);
   strcpy(afToName,toname);
   Copy4D(&afTo4D,to4d);
   strcpy(afSubject,subject);

   afRawPrepareMessage();

   afLines=0;
   afPart=1;
}

void afRawPrepareMessage(void)
{
   struct DateStamp ds;
   UBYTE *chrskludge="\x01CHRS: LATIN-1 2\x0d";

   jbNewList(&MemMessage.TextChunks);

   Copy4D(&MemMessage.DestNode,&afTo4D);
   Copy4D(&MemMessage.OrigNode,&afFrom4D);

   strcpy(MemMessage.To,afToName);
   strcpy(MemMessage.From,afFromName);
   strcpy(MemMessage.Subject,afSubject);

   DateStamp(&ds);
   MakeFidoDate(&ds,MemMessage.DateTime);

   MemMessage.Attr = PVT;
   MemMessage.Cost = 0;

   MemMessage.BadReason[0]=0;
   MemMessage.MSGID[0]=0;
   MemMessage.REPLY[0]=0;

   MakeNetmailKludges(&MemMessage.TextChunks,&afFrom4D,&afTo4D,cfg_Flags & CFG_ADDTID);
   AddChunkList(&MemMessage.TextChunks,chrskludge,strlen(chrskludge));
}

void afAddLine(UBYTE *fmt,...)
{
   va_list args;
   UBYTE buf[200];

   if(afLines >= cfg_AreaFixMaxLines-2 && cfg_AreaFixMaxLines!=0)
   {
      strcpy(buf,"\x0d(Continued in next message)\x0d");
      AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

      sprintf(MemMessage.Subject,"%s (part %ld)",afSubject,afPart);
      afSendMessage();
      jbFreeList(&MemMessage.TextChunks,MemMessage.TextChunks.First,sizeof(struct TextChunk));

      afRawPrepareMessage();

      strcpy(buf,"(Continued from previous message)\x0d\x0d");
      AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

      afLines=2;
      afPart++;
   }

   va_start(args, fmt);
   vsprintf(buf,fmt,args);
   va_end(args);

   strcat(buf,"\x0d");
   AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

   afLines++;
}

void afSendMessage(void)
{
   if(afPart != 1)
      sprintf(MemMessage.Subject,"%s (part %ld)",afSubject,afPart);

   isbounce=TRUE;
   HandleNetmail();
   isbounce=FALSE;
}

RemoteAreafix(UBYTE *area,struct ConfigNode *node)
{
   struct RemoteAFCommand *cmd;

   if(!(cmd=(struct RemoteAFCommand *)AllocMem(sizeof(struct RemoteAFCommand),MEMF_CLEAR)))
   {
      nomem=TRUE;
      return;
   }

   strncpy(cmd->Command,area,70);
   cmd->Command[70]=0;
   jbAddNode(&node->RemoteAFList,(struct jbNode *)cmd);
}

void SendRemoteAreafix(void)
{
   struct Route *tmproute;
   struct TextChunk *firstchunk,*lastchunk;
   struct ConfigNode *node;
   struct RemoteAFCommand *cmd;
   UBYTE buf[200];
   struct DateStamp ds;
   struct MemMessage mmbak;
   UBYTE *chrskludge="\x01CHRS: LATIN-1 2\x0d";

   for(node=CNodeList.First;node;node=node->Next)
      if(node->RemoteAFList.First)
      {
         CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));
         firstchunk=MemMessage.TextChunks.First;
         lastchunk=MemMessage.TextChunks.Last;
         jbNewList(&MemMessage.TextChunks);

         for(tmproute=RouteList.First;tmproute;tmproute=tmproute->Next)
            if(Compare4DPat(&tmproute->Pattern,&node->Node)==0) break;

         MakeNetmailKludges(&MemMessage.TextChunks,&tmproute->Aka->Node,&node->Node,cfg_Flags & CFG_ADDTID);
         AddChunkList(&MemMessage.TextChunks,chrskludge,strlen(chrskludge));

         Copy4D(&MemMessage.DestNode,&node->Node);
         Copy4D(&MemMessage.OrigNode,&tmproute->Aka->Node);

         strcpy(MemMessage.To,node->RemoteAFName);
         strcpy(MemMessage.From,cfg_Sysop);
         strcpy(MemMessage.Subject,node->RemoteAFPw);

         DateStamp(&ds);
         MakeFidoDate(&ds,MemMessage.DateTime);

         MemMessage.Attr = PVT;
         MemMessage.Cost = 0;

         MemMessage.BadReason[0]=0;
         MemMessage.MSGID[0]=0;
         MemMessage.REPLY[0]=0;

         for(cmd=node->RemoteAFList.First;cmd;cmd=cmd->Next)
         {
            sprintf(buf,"%s\x0d",cmd->Command);
            AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));
         }

         sprintf(buf,"---\x0dGenerated by CrashMail "VERSION"\x0d");
         AddChunkList(&MemMessage.TextChunks,buf,strlen(buf));

         isbounce=TRUE;
         HandleNetmail();
         isbounce=FALSE;

         CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
         firstchunk=MemMessage.TextChunks.First;
         lastchunk=MemMessage.TextChunks.Last;

         jbFreeList(&node->RemoteAFList,node->RemoteAFList.First,sizeof(struct RemoteAFCommand));
      }
}

BOOL CheckFlags(UBYTE group,UBYTE *node)
{
   UBYTE c;

   for(c=0;c<strlen(node);c++)
   {
      if(group==node[c])
         return(TRUE);
    }

   return(FALSE);
}

struct Arealist *FindForward(UBYTE *tagname,UBYTE *flags)
{
   struct Arealist *arealist;
   UBYTE buf[200];
   ULONG c;
   BPTR fh;

   for(arealist=ArealistList.First;arealist;arealist=arealist->Next)
   {
      if((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group,flags))
      {
         if(fh=Open(arealist->AreaFile,MODE_OLDFILE))
         {
            while(FGets(fh,buf,199))
            {
               for(c=0;buf[c]>32;c++);
               buf[c]=0;

               if(stricmp(buf,tagname)==0)
               {
                  Close(fh);
                  return(arealist);
               }
            }
            Close(fh);
         }
      }
   }
   return(NULL);
}

void DoSendAFList(short type,struct ConfigNode *cnode)
{
   struct Route *tmproute;

   for(tmproute=RouteList.First;tmproute;tmproute=tmproute->Next)
      if(Compare4DPat(&tmproute->Pattern,&cnode->Node)==0) break;

   if(!tmproute)
   {
      LogWrite(1,TOSSINGERR,"No route found for %ld:%ld/%ld.%ld",
         cnode->Node.Zone,
         cnode->Node.Net,
         cnode->Node.Node,
         cnode->Node.Point);

      return;
   }

   switch(type)
   {
      case SENDLIST_FULL:
         rawSendList(SENDLIST_FULL,&tmproute->Aka->Node,cnode->SysopName,cnode);
         break;

      case SENDLIST_QUERY:
         rawSendList(SENDLIST_QUERY,&tmproute->Aka->Node,cnode->SysopName,cnode);
         break;

      case SENDLIST_UNLINKED:
         rawSendList(SENDLIST_UNLINKED,&tmproute->Aka->Node,cnode->SysopName,cnode);
         break;

      case SENDLIST_INFO:
         rawSendInfo(&tmproute->Aka->Node,cnode->SysopName,cnode);
         break;

      case SENDLIST_HELP:
         rawSendHelp(&tmproute->Aka->Node,cnode->SysopName,cnode);
         break;
   }
}

BOOL SendAFList(short type,UBYTE *node)
{
   struct Node4D n4d;
   struct ConfigNode *cnode;
   struct MemMessage mmbak;

   if(!InitSession())
      return(FALSE);

   if(stricmp(node,"ALL")==0)
   {
      CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));

      for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
         if(cnode->Flags & NODE_NOTIFY) DoSendAFList(type,cnode);

      CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
   }
   else
   {
      if(!Parse4D(node,&n4d))
      {
         LogWrite(1,USERERR,"Invalid node number \"%s\"",node);
         return(FALSE);
      }

      for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
         if(Compare4D(&cnode->Node,&n4d)==0) break;

      if(cnode)
      {
         CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));

         DoSendAFList(type,cnode);

         CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
      }
      else
      {
         LogWrite(1,USERERR,"Unknown node %lu:%lu/%lu.%lu",n4d.Zone,n4d.Net,n4d.Node,n4d.Point);
         return(FALSE);
      }
   }

   FinishSession();

   if(nomem || exitprg)
      return(FALSE);

   return(TRUE);
}

Rescan(struct Area *area,ULONG num)
{
   ULONG res;
   struct MemMessage mmbak;

   CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));

   jbNewList(&MemMessage.TextChunks);
   jbNewList(&MemMessage.SeenBy);
   jbNewList(&MemMessage.Path);

   MemMessage.TextChunks.First = (struct TextChunk *)AllocMem(sizeof(struct TextChunk),MEMF_ANY);
   MemMessage.SeenBy.First     = (struct Nodes2D *)AllocMem(sizeof(struct Nodes2D),MEMF_ANY);
   MemMessage.Path.First       = (struct Nodes2D *)AllocMem(sizeof(struct Path),MEMF_ANY);

   MemMessage.TextChunks.Last  = MemMessage.TextChunks.First;
   MemMessage.SeenBy.Last      = MemMessage.SeenBy.First;
   MemMessage.Path.Last        = MemMessage.Path.First;

   MemMessage.TextChunks.First->Next=NULL;
   MemMessage.SeenBy.First->Next=NULL;
   MemMessage.Path.First->Next=NULL;

   if(MemMessage.TextChunks.First && MemMessage.SeenBy.First && MemMessage.Path.First)
   {
      switch(area->AreaType)
      {
         case AREATYPE_UMS: res=RescanUMS(area,num);
                            break;
         case AREATYPE_MSG: res=RescanMSG(area,num);
                            break;
      }
   }
   else
   {
      nomem=TRUE;
   }

   if(MemMessage.TextChunks.First)
      FreeMem(MemMessage.TextChunks.First,sizeof(struct TextChunk));

   if(MemMessage.SeenBy.First)
      FreeMem(MemMessage.SeenBy.First,sizeof(struct Nodes2D));

   if(MemMessage.Path.First)
      FreeMem(MemMessage.Path.First,sizeof(struct Path));

   CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));

   return(res);
}

BOOL RemoveArea(UBYTE *areaname)
{
   struct MemMessage mmbak;
   struct Area *area;

   CopyMem(&MemMessage,&mmbak,sizeof(struct MemMessage));

   if(!InitSession())
      return(FALSE);

   for(area=AreaList.First;area;area=area->Next)
      if(!(area->Flags & AREA_BAD) && !(area->Flags & AREA_NETMAIL) && !(area->Flags & AREA_DEFAULT))
         if(stricmp(areaname,area->Tagname)==0) break;

   if(!area)
   {
      LogWrite(1,USERERR,"Unknown area %s",areaname);
      CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));
      return(FALSE);
   }

   LogWrite(1,AREAFIX,"AreaFix: Removing area %s",area->Tagname);
   rawRemoveArea(area);

   FinishSession();

   CopyMem(&mmbak,&MemMessage,sizeof(struct MemMessage));

   return(TRUE);
}

BOOL ParseRescan(UBYTE *areaname,UBYTE *node,ULONG max)
{
   struct Area *area;
   struct ConfigNode *cnode;
   struct Node4D n4d;
   ULONG num;

   if(!Parse4D(node,&n4d))
   {
      LogWrite(1,USERERR,"Invalid node number %s",node);
      return(FALSE);
   }

   for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
      if(Compare4D(&cnode->Node,&n4d)==0) break;

   if(!cnode)
   {
      LogWrite(1,USERERR,"Unknown node %lu:%lu/%lu.%lu",n4d.Zone,n4d.Net,n4d.Node,n4d.Point);
      return(FALSE);
   }

   for(area=AreaList.First;area;area=area->Next)
      if(!(area->Flags & AREA_BAD) && !(area->Flags & AREA_NETMAIL) && !(area->Flags & AREA_DEFAULT))
         if(stricmp(areaname,area->Tagname)==0) break;

   if(!area)
   {
      LogWrite(1,USERERR,"Unknown area %s",areaname);
      return(FALSE);
   }

   if(area->AreaType == AREATYPE_UMS || area->AreaType == AREATYPE_MSG)
   {
      if(!InitSession())
         return(FALSE);

      LogWrite(4,TOSSINGINFO,"Rescanning %s for %lu:%lu/%lu.%lu",areaname,n4d.Zone,n4d.Net,n4d.Node,n4d.Point);

      RescanNode=cnode;
      num=Rescan(area,max);
      RescanNode=NULL;

      if(!exitprg && !nomem && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
         LogWrite(4,TOSSINGINFO,"Rescanned %lu messages",num);

      FinishSession();
   }
   else
   {
      LogWrite(1,USERERR,"Can't rescan %s, area is pass-through",areaname);
      return(FALSE);
   }

   return(TRUE);
}

void convertstar(UBYTE *buf,ULONG len)
{
   UBYTE tmp[100];
   ULONG c,d;

   d=0;

   for(c=0;buf[c]!=0 && d!=79;c++)
   {
      if(buf[c]=='*')
      {
         tmp[d++]='#';
         if(d!=79) tmp[d++]='?';
      }
      else
      {
         tmp[d++]=buf[c];
      }
   }

   tmp[d]=0;

   mystrncpy(buf,tmp,len);
}
