/*
* Copyright (C) 1998 Brandon Long <blong@fiction.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Copied from my nntp patch for mutt 0.95, and modified */
#include "gbuffy.h"
#include "msocket.h"
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
typedef struct
{
int first;
int last;
} NEWSRC_ENTRY;
typedef struct _newsrc_line
{
NEWSRC_ENTRY *entries;
int num; /* number of used entries */
int max; /* number of allocated entries */
int subscribed : 1; /* subscribed to group */
} NEWSRC_LINE;
typedef struct
{
char *filename;
time_t mtime;
off_t size;
char *newsgroup;
NEWSRC_LINE line;
} NEWSRC;
static NEWSRC *newsrc_info = NULL;
static int num_newsrc = 0;
static int nntp_parse_newsrc_line (NEWSRC *newsrc, char *line)
{
int x = 0;
char *p = line;
char *b, *h;
while (*p)
{
if (*p == ',')
x++;
p++;
}
x++;
newsrc->line.entries = (NEWSRC_ENTRY *) safe_calloc (x*2, sizeof (NEWSRC_ENTRY));
newsrc->line.max = x*2;
p = line;
while (*p && (*p != ':' && *p != '!')) p++;
if (!*p) return -1;
if (*p == ':')
newsrc->line.subscribed = 1;
*p = '\0';
/* line now points to the newsgroup name */
p++;
b = p;
x = 0;
while (*b)
{
while (*p && *p != ',') p++;
if (*p)
{
*p = '\0';
p++;
}
if ((h = strchr(b, '-')))
{
*h = '\0';
h++;
newsrc->line.entries[x].first = atoi(b);
newsrc->line.entries[x].last = atoi(h);
}
else
{
newsrc->line.entries[x].first = atoi(b);
newsrc->line.entries[x].last = newsrc->line.entries[x].first;
}
b = p;
x++;
}
newsrc->line.num = x;
dprint (1, (debugfile, "parse_line: Newsgroup %s\n", line));
return 0;
}
static int slurp_newsrc (NEWSRC *newsrc, char *group, char *filename)
{
FILE *fp;
char buf[HUGE_STRING];
struct stat sb;
int grouplen = strlen (group);
if (newsrc->filename == NULL)
newsrc->filename = safe_strdup (filename);
if (newsrc->newsgroup == NULL)
newsrc->newsgroup = safe_strdup (group);
if ((fp = fopen (filename, "r")) == NULL)
{
return (-1);
}
#if 0
/* Hmm, should we lock this file? */
if (mx_lock_file (filename, fileno (fp), 0, 1, 1))
{
fclose (fp);
return (-1);
}
#endif
stat (filename, &sb);
newsrc->size = sb.st_size;
newsrc->mtime = sb.st_mtime;
while (fgets (buf, sizeof (buf), fp))
{
if (!strncmp (buf, group, grouplen) &&
((buf[grouplen] == ':') || (buf[grouplen] == '!')))
{
nntp_parse_newsrc_line (newsrc, buf);
break;
}
}
#if 0
mx_unlock_file (filename, fileno (fp));
#endif
fclose (fp);
return 0;
}
/*
* Automatically loads a newsrc into memory, if necessary.
* Checks the size/mtime of a newsrc file, if it doesn't match, load
* again. Hmm, if a system has broken mtimes, this might mean the file
* is reloaded every time, which we'd have to fix.
*
* a newsrc file is a line per newsgroup, with the newsgroup, then a
* ':' denoting subscribed or '!' denoting unsubscribed, then a
* comma separated list of article numbers and ranges.
*/
NEWSRC *select_newsrc (char *group, char *filename)
{
char file[_POSIX_PATH_MAX];
int x;
strcpy (file, filename);
gbuffy_expand_path (file, sizeof (file));
for (x = 0; x < num_newsrc; x++)
{
if (!strcmp (file, newsrc_info[x].filename) &&
!strcmp (group, newsrc_info[x].newsgroup))
{
struct stat sb;
stat (file, &sb);
if (newsrc_info[x].size == sb.st_size &&
newsrc_info[x].mtime == sb.st_mtime)
{
return &newsrc_info[x];
}
else
{
FREE (&newsrc_info[x].filename);
FREE (&newsrc_info[x].line.entries);
slurp_newsrc (&newsrc_info[x], group, file);
return &newsrc_info[x];
}
}
}
/* New newsrc */
x = num_newsrc;
if (num_newsrc == 0)
{
num_newsrc++;
newsrc_info = (NEWSRC *) safe_calloc (1, sizeof (NEWSRC));
}
else
{
num_newsrc++;
safe_realloc ((void *)&newsrc_info, sizeof (NEWSRC) * num_newsrc);
newsrc_info[num_newsrc-1].filename = NULL;
newsrc_info[num_newsrc-1].mtime = 0;
newsrc_info[num_newsrc-1].size = 0;
newsrc_info[num_newsrc-1].newsgroup = NULL;
newsrc_info[num_newsrc-1].line.entries = NULL;
newsrc_info[num_newsrc-1].line.num = 0;
newsrc_info[num_newsrc-1].line.max = 0;
newsrc_info[num_newsrc-1].line.subscribed = 0;
}
slurp_newsrc (&newsrc_info[x], group, file);
return &newsrc_info[x];
}
/*
* full status flags are not supported by nntp, but we can fake some
* of them. This is how:
* Read = a read message number is in the .newsrc
* New = a message is new since we last read this newsgroup
* Old = anything else
* So, Read is marked as such in the newsrc, old is anything that is
* "skipped" in the newsrc, and new is anything not in the newsrc.
* By skipped, I mean before the last unread message
* Returns 0 if not read, 1 if old, 2 if read
*/
int nntp_get_status (char *group, char *npath, int article_num)
{
NEWSRC *newsrc;
NEWSRC_LINE *data;
int x;
newsrc = select_newsrc (group, npath);
data = &newsrc->line;
if (data == NULL)
{
dprint (1, (debugfile, "newsgroup %s not found\n", group));
return 0;
}
for (x = 0; x < data->num; x++)
{
if ((article_num >= data->entries[x].first) &&
(article_num <= data->entries[x].last))
{
return 2;
}
}
/* Old articles are articles which aren't read but an article after them
* has been read */
if (article_num < data->entries[data->num - 1].last)
return 1;
return 0;
}
/*
* for nntp_context, we need the "oldest read" message, which one would
* think would be the first in the newsrc line, but its actually the
* last of the first pair (the first pair is usually 1-<first read>)
*/
int nntp_first_read (char *group, char *npath)
{
NEWSRC *newsrc;
NEWSRC_LINE *data;
newsrc = select_newsrc (group, npath);
data = &newsrc->line;
if (data == NULL)
return 0;
if (data->num)
return (data->entries[0].last);
else
return 0;
}
/*
* for buffy, we need the last read, which is the last entry on the line
*/
int nntp_last_read (char *group, char *npath)
{
NEWSRC *newsrc;
NEWSRC_LINE *data;
newsrc = select_newsrc (group, npath);
data = &newsrc->line;
if (data == NULL)
return 0;
if (data->num)
return (data->entries[data->num-1].last);
else
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1