/********************************************************
 * File: memmessage.c
 * Created at Sun Jan 28 22:10:31 MSK 2001 by raorn // raorn@binec.ru
 * Memorized message handling
 * $Id: memmessage.c,v 1.8 2001/12/24 01:02:55 raorn Exp $
 *******************************************************/
#include "crashecho.h"

bool mmAddNodes2DList(struct jbList *list, ushort net, ushort node)
{
  /* Add a node to SEEN-BY list */
  struct Nodes2D *tmplist;
  ushort num;

  /* Check if it already exists */
  for (tmplist = (struct Nodes2D *) list->First; tmplist; tmplist = tmplist->Next) {
    for (num = 0; num < tmplist->Nodes; num++)
      if (tmplist->Net[num] == net && tmplist->Node[num] == node)
        return (TRUE);
  }

  tmplist = (struct Nodes2D *) list->Last;

  if (tmplist && tmplist->Nodes == PKT_NUM2D)
    tmplist = NULL;

  if (!tmplist) {
    if (!(tmplist = (struct Nodes2D *) malloc(sizeof(struct Nodes2D)))) {
      nomem = TRUE;
      return (FALSE);
    }

    jbAddNode(list, (struct jbNode *) tmplist);
    tmplist->Nodes = 0;
    tmplist->Next = NULL;
  }

  tmplist->Net[tmplist->Nodes] = net;
  tmplist->Node[tmplist->Nodes] = node;
  tmplist->Nodes++;

  return (TRUE);
}

void mmRemNodes2DList(struct jbList *list, ushort net, ushort node)
{
  /* Rem a node from SEEN-BY list */
  struct Nodes2D *tmplist;
  ushort num;

  for (tmplist = (struct Nodes2D *) list->First; tmplist; tmplist = tmplist->Next)
    for (num = 0; num < tmplist->Nodes; num++)
      if (tmplist->Net[num] == net && tmplist->Node[num] == num) {
        tmplist->Net[num] = 0;
        tmplist->Node[num] = 0;
      }
}

void mmRemNodes2DListPat(struct jbList *list, struct Node2DPat *pat)
{
  struct Nodes2D *tmplist;
  ushort num;

  for (tmplist = (struct Nodes2D *) list->First; tmplist; tmplist = tmplist->Next)
    for (num = 0; num < tmplist->Nodes; num++) {
      if (Compare2DPat(pat, tmplist->Net[num], tmplist->Node[num]) == 0) {
        tmplist->Net[num] = 0;
        tmplist->Node[num] = 0;
      }
    }
}

bool mmAddPath(uchar * str, struct jbList *list)
{
  /* Add a node string to PATH list */
  struct Path *path;

  if (str[0] == 0)
    return (TRUE);

  path = (struct Path *) list->Last;

  if (path && path->Paths == PKT_NUMPATH)
    path = NULL;

  if (!path) {
    if (!(path = (struct Path *) malloc(sizeof(struct Path)))) {
      nomem = TRUE;
      return (FALSE);
    }

    jbAddNode(list, (struct jbNode *) path);
    path->Paths = 0;
    path->Next = NULL;
  }

  mystrncpy(path->Path[path->Paths], str, 100);
  striptrail(path->Path[path->Paths]);
  path->Paths++;

  return (TRUE);
}

bool mmAddBuf(struct jbList * chunklist, uchar * buf, ulong len, ulong where)
{
  struct TextChunk *chunk, *lastchunk, *tmpchunk;

  if (!(chunk = (struct TextChunk *) calloc(1, sizeof(struct TextChunk)))) {
    nomem = TRUE;
    return (FALSE);
  }

  if (!(chunk->Data = calloc(1, len + 1))) {
    nomem = TRUE;
    return (FALSE);
  }

  memcpy(chunk->Data, buf, (size_t) len);

  switch(where) {
  case MM_ADD:
    jbAddNode(chunklist, (struct jbNode *) chunk);
    break;
  case MM_HEAD:
    chunk->Next = (struct TextChunk *) chunklist->First;
    chunklist->First = (struct jbNode *) chunk;
    if (!chunk->Next)
      chunklist->Last = (struct jbNode *) chunk;
    break;
  case MM_KLUDGE:
    lastchunk = NULL;
    /* Find first non-kudge line */
    for (tmpchunk = (struct TextChunk *) chunklist->First; tmpchunk; lastchunk = tmpchunk, tmpchunk = tmpchunk->Next)
      if (tmpchunk->Data && tmpchunk->Data[0] != '\001')
        break;

    /* Add chunk after lastchunk */
    if (lastchunk) {
      chunk->Next = lastchunk->Next;
      lastchunk->Next = chunk;
    } else {
      chunk->Next = (struct TextChunk *) chunklist->First;
      chunklist->First = (struct jbNode *) chunk;
    }
    if (!chunk->Next)
      chunklist->Last = (struct jbNode *) chunk;
    break;
  default:
    free(chunk->Data);
    free(chunk);
    return FALSE;
    break;
  }

  return (TRUE);
}

struct MemMessage *mmAlloc(void)
{
  struct MemMessage *mm;

  if (!(mm = calloc(1, sizeof(struct MemMessage)))) {
    nomem = TRUE;
    return NULL;
  }

  jbNewList(&mm->TextChunks);
  jbNewList(&mm->SeenBy);
  jbNewList(&mm->Path);

  return mm;
}

void mmClear(struct MemMessage *mm)
{
  struct TextChunk *chunk;
  struct Node4D null4d = { 0, 0, 0, 0 };

  Copy4D(&mm->OrigNode, &null4d);
  Copy4D(&mm->DestNode, &null4d);
  Copy4D(&mm->PktOrig, &null4d);
  Copy4D(&mm->PktDest, &null4d);

  mm->Area[0] = 0;

  mm->To[0] = 0;
  mm->From[0] = 0;
  mm->Subject[0] = 0;
  mm->DateTime[0] = 0;

  mm->MSGID[0] = 0;
  mm->REPLY[0] = 0;

  mm->Attr = 0;
  mm->Cost = 0;

  mm->Type = 0;
  mm->Rescanned = FALSE;
  mm->Changed = FALSE;
  mm->Deleted = FALSE;
  mm->no_security = FALSE;

  for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk; chunk = chunk->Next)
    if (chunk->Data)
      free(chunk->Data);

  jbFreeList(&mm->TextChunks);
  jbFreeList(&mm->SeenBy);
  jbFreeList(&mm->Path);
}

void mmFree(struct MemMessage *mm)
{
  struct TextChunk *chunk;

  for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk; chunk = chunk->Next)
    if (chunk->Data)
      free(chunk->Data);

  jbFreeList(&mm->TextChunks);
  jbFreeList(&mm->SeenBy);
  jbFreeList(&mm->Path);

  free(mm);
}

struct TempSort {
  ushort Net;
  ushort Node;
};

int CompareSort(const void *t1, const void *t2)
{
  if (((struct TempSort *) t1)->Net > ((struct TempSort *) t2)->Net)
    return (1);

  if (((struct TempSort *) t1)->Net < ((struct TempSort *) t2)->Net)
    return (-1);

  if (((struct TempSort *) t1)->Node > ((struct TempSort *) t2)->Node)
    return (1);

  if (((struct TempSort *) t1)->Node < ((struct TempSort *) t2)->Node)
    return (-1);

  return (0);
}

bool mmSortNodes2D(struct jbList * list)
{
  struct Nodes2D *tmp;
  struct TempSort *sorttemp;
  ulong nodes = 0;
  ulong c, d;

  for (tmp = (struct Nodes2D *) list->First; tmp; tmp = tmp->Next)
    nodes += tmp->Nodes;

  if (nodes == 0)
    return (TRUE);

  if (!(sorttemp = (struct TempSort *) malloc(sizeof(struct TempSort) * nodes))) {
    nomem = TRUE;
    return (FALSE);
  }

  d = 0;

  for (tmp = (struct Nodes2D *) list->First; tmp; tmp = tmp->Next)
    for (c = 0; c < tmp->Nodes; c++) {
      sorttemp[d].Net = tmp->Net[c];
      sorttemp[d].Node = tmp->Node[c];
      d++;
    }

  qsort(sorttemp, (size_t) nodes, sizeof(struct TempSort), CompareSort);

  tmp = (struct Nodes2D *) list->First;
  tmp->Nodes = 0;

  for (c = 0; c < nodes; c++) {
    if (tmp->Nodes == PKT_NUM2D) {
      tmp = tmp->Next;
      tmp->Nodes = 0;
    }
    tmp->Net[tmp->Nodes] = sorttemp[c].Net;
    tmp->Node[tmp->Nodes] = sorttemp[c].Node;
    tmp->Nodes++;
  }

  free(sorttemp);

  return (TRUE);
}

bool AddSeenby(uchar * str, struct jbList * list)
{
  /* Add a node string to SEEN-BY list */
  ulong c, d;
  uchar buf[60];
  ushort lastnet, num;

  c = 0;
  lastnet = 0;

  while (str[c] != 13 && str[c] != 0) {
    d = 0;

    while (str[c] != 0 && str[c] != 13 && str[c] != 32 && d < 59)
      buf[d++] = str[c++];

    buf[d] = 0;
    while (str[c] == 32)
      c++;

    if (buf[0]) {
      num = 0;
      d = 0;

      while (buf[d] >= '0' && buf[d] <= '9') {
        num *= 10;
        num += buf[d] - '0';
        d++;
      }

      if (buf[d] == '/') {
        lastnet = num;
        num = atoi(&buf[d + 1]);

        if (!mmAddNodes2DList(list, lastnet, num))
          return (FALSE);
      } else if (buf[d] == 0) {
        if (!mmAddNodes2DList(list, lastnet, num))
          return (FALSE);
      }
    }
  }

  return (TRUE);
}

void ProcessKludge(struct MemMessage *mm, uchar * kludge)
{
  struct Node4D node;
  uchar buf[60];
  ulong c, d;

  if (strncmp(kludge, "\001RESCANNED", 10) == 0) {
    mm->Rescanned = TRUE;
  } else if (strncmp(kludge, "\001MSGID:", 7) == 0) {
    for (d = 0, c = 8; d < 79 && kludge[c] != 13 && kludge[c] != 0; c++, d++)
      mm->MSGID[d] = kludge[c];

    mm->MSGID[d] = 0;
  } else if (strncmp(kludge, "\001REPLY:", 7) == 0) {
    for (d = 0, c = 8; d < 79 && kludge[c] != 13 && kludge[c] != 0; c++, d++)
      mm->REPLY[d] = kludge[c];

    mm->REPLY[d] = 0;
  } else if (mm->Area[0] == 0) {
    if (strncmp(kludge, "\001FMPT", 5) == 0)
      mm->OrigNode.Point = atoi(&kludge[6]);
    else if (strncmp(kludge, "\001TOPT", 5) == 0)
      mm->DestNode.Point = atoi(&kludge[6]);
    else if (strncmp(kludge, "\001INTL", 5) == 0) {
      if (kludge[5] == ':')
        c = 7;
      else
        c = 6;

      for (d = 0; d < 59 && kludge[c] != 32 && kludge[c] != 0; c++, d++)
        buf[d] = kludge[c];

      buf[d] = 0;

      if (Parse4D(buf, &node)) {
        mm->DestNode.Zone = node.Zone;
        mm->DestNode.Net = node.Net;
        mm->DestNode.Node = node.Node;
      }

      if (kludge[c] == 32)
        c++;

      for (d = 0; d < 59 && kludge[c] != 32 && kludge[c] != 0 && kludge[c] != 13; c++, d++)
        buf[d] = kludge[c];

      buf[d] = 0;

      if (Parse4D(buf, &node)) {
        mm->OrigNode.Zone = node.Zone;
        mm->OrigNode.Net = node.Net;
        mm->OrigNode.Node = node.Node;
      }
    }
  }
}

bool mmAddLine(struct MemMessage *mm, uchar * buf, ulong where)
{
  if (mm->Area[0] && strncmp(buf, "SEEN-BY:", 8) == 0) {
    return AddSeenby(&buf[9], &mm->SeenBy);
  } else if (mm->Area[0] && strncmp(buf, "\001PATH:", 6) == 0) {
    return mmAddPath(&buf[7], &mm->Path);
  } else if (buf[0] == 1) {
    if (strncmp(buf, "\001RESCANNED", 10) == 0) {
      mm->Rescanned = TRUE;
      return TRUE;
    } else
      ProcessKludge(mm, buf);
  }

  return mmAddBuf(&mm->TextChunks, buf, (ulong) strlen(buf), where);
}

uchar *mmMakeSeenByBuf(struct jbList * list)
{
  uchar *text;
  ulong seenbys, size;
  struct Nodes2D *nodes;
  ulong c;
  ushort lastnet;
  uchar buf[100], buf2[50], buf3[20];

  /* Count seenbys */

  seenbys = 0;

  for (nodes = (struct Nodes2D *) list->First; nodes; nodes = nodes->Next)
    seenbys += nodes->Nodes;

  /*
   * We allocate generously. Maximum length per seenby: <space>12345/12345:
   * 12 characters 79 characters - "SEEN-BY:" = 71 characters which makes
   * 71/12 seenbys/line = 5
   *
   * Fortunately even with this generous calculation will not result in huge
   * memory areas, 500 seenbys (which is a lot) would need 8000 bytes...
   *
   */

  size = (seenbys / 5 + 1) * 80;

  /* Allocate our memory block */
  if (!(text = malloc(size)))
    return (NULL);

  text[0] = 0;

  strcpy(buf, "SEEN-BY:");
  lastnet = 0;

  for (nodes = (struct Nodes2D *) list->First; nodes; nodes = nodes->Next) {
    for (c = 0; c < nodes->Nodes; c++) {
      if (nodes->Net[c] != 0 || nodes->Node[c] != 0) {
        strcpy(buf2, " ");

        if (nodes->Net[c] != lastnet) {
          snprintf(buf3, 20, "%u/", nodes->Net[c]);
          strcat(buf2, buf3);
        }

        snprintf(buf3, 20, "%u", nodes->Node[c]);
        strcat(buf2, buf3);

        lastnet = nodes->Net[c];

        if (strlen(buf) + strlen(buf2) > 77) {
          strcat(text, buf);
          strcat(text, "\015");

          strcpy(buf, "SEEN-BY:");
          snprintf(buf2, 50, " %u/%u", nodes->Net[c], nodes->Node[c]);
          lastnet = nodes->Net[c];

          strcat(buf, buf2);
        } else {
          strcat(buf, buf2);
        }
      }
    }
  }

  if (strlen(buf) > 8) {
    strcat(text, buf);
    strcat(text, "\015");
  }

  return (text);
}


syntax highlighted by Code2HTML, v. 0.9.1