1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: news.c 155 2006-09-29 23:28:46Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
#include "../pith/headers.h"
19
#include "../pith/news.h"
20
#include "../pith/state.h"
21
#include "../pith/conf.h"
22
#include "../pith/context.h"
23
#include "../pith/stream.h"
24
#include "../pith/util.h"
27
typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;
33
NgCacheReturns chk_newsgrp_cache(char *);
34
void add_newsgrp_cache(char *, NgCacheReturns);
37
/*----------------------------------------------------------------------
38
Function to see if a given MAILSTREAM mailbox is in the news namespace
40
Input: stream -- mail stream to test
45
ns_test(char *mailbox, char *namespace)
50
return(!struncmp(mailbox + 1, namespace, strlen(namespace)));
56
if(mail_valid_net_parse(mailbox, &mbx))
57
return(ns_test(mbx.mailbox, namespace));
72
news_in_folders(struct variable *var)
74
int i, found_news = 0;
77
if(!(var && var->current_val.l))
80
for(i=0; !found_news && var->current_val.l[i]; i++){
81
if(tc = new_context(var->current_val.l[i], NULL)){
82
if(tc->use & CNTXT_NEWS)
93
/*----------------------------------------------------------------------
94
Verify and canonicalize news groups names.
95
Called from the message composer
97
Args: given_group -- List of groups typed by user
98
expanded_group -- pointer to point to expanded list, which will be
99
allocated here and freed in caller. If this is
100
NULL, don't attempt to validate.
101
error -- pointer to store error message
102
fcc -- pointer to point to fcc, which will be
103
allocated here and freed in caller
105
Returns: 0 if all is OK
106
-1 if addresses weren't valid
108
Test the given list of newstroups against those recognized by our nntp
109
servers. Testing by actually trying to open the list is much cheaper, both
110
in bandwidth and memory, than yanking the whole list across the wire.
113
news_grouper(char *given_group, char **expanded_group, char **error,
114
char **fccptr, void (*delay_warning)(void))
116
char ng_error[90], *p1, *p2, *name, *end, *ep, **server,
118
int expanded_len = 0, num_in_error = 0, cnt_errs;
120
MAILSTREAM *stream = NULL;
123
NgCacheReturns found;
124
struct ng_list *next;
125
}*nglist = NULL, **ntmpp, *ntmp;
127
static int no_servers = 0;
131
"- news_build - (%s)\n", given_group ? given_group : "nul"));
138
/*------ parse given entries into a list ----*/
140
for(name = given_group; *name; name = end){
142
/* find start of next group name */
143
while(*name && (isspace((unsigned char)*name) || *name == ','))
146
/* find end of group name */
148
while(*end && !isspace((unsigned char)*end) && *end != ',')
152
*ntmpp = (struct ng_list *)fs_get(sizeof(struct ng_list));
153
(*ntmpp)->next = NULL;
154
(*ntmpp)->found = NotChecked;
155
(*ntmpp)->groupname = fs_get(end - name + 1);
156
strncpy((*ntmpp)->groupname, name, end - name);
157
(*ntmpp)->groupname[end - name] = '\0';
158
ntmpp = &(*ntmpp)->next;
160
break; /* no need to continue if just doing fcc */
165
* If fcc is not set or is set to default, then replace it if
166
* one of the recipient rules is in effect.
169
if((ps_global->fcc_rule == FCC_RULE_RECIP ||
170
ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
171
(nglist && nglist->groupname)){
173
fs_give((void **) fccptr);
175
*fccptr = cpystr(nglist->groupname);
177
else if(!*fccptr) /* already default otherwise */
178
*fccptr = cpystr(ps_global->VAR_DEFAULT_FCC);
183
*expanded_group = cpystr("");
191
for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
192
dprint((9, "Parsed group: --[%s]--\n",
193
ntmp->groupname ? ntmp->groupname : "?"));
196
/* If we are doing validation */
197
if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
198
int need_to_talk_to_server = 0;
201
* First check our cache of validated newsgroups to see if we even
202
* have to open a stream.
204
for(ntmp = nglist; ntmp; ntmp = ntmp->next){
205
ntmp->found = chk_newsgrp_cache(ntmp->groupname);
206
if(ntmp->found == NotInCache)
207
need_to_talk_to_server++;
210
if(need_to_talk_to_server){
219
* Build a stream to the first server that'll talk to us...
221
for(server = ps_global->VAR_NNTP_SERVER;
222
server && *server && **server;
224
snprintf(ng_ref, sizeof(ng_ref), "{%.*s/nntp}#news.",
225
sizeof(ng_ref)-30, *server);
226
if(stream = pine_mail_open(stream, ng_ref,
227
OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
231
if(!server || !stream){
235
/* don't say this over and over */
237
if(!server || !*server || !**server)
240
*error = cpystr(no_servers
241
/* TRANSLATORS: groups refers to news groups */
242
? _("Can't validate groups. No servers defined")
243
/* TRANSLATORS: groups refers to news groups */
244
: _("Can't validate groups. No servers responding"));
248
*error = cpystr((!server || !*server || !**server)
249
? _("No servers defined for posting to newsgroups")
250
/* TRANSLATORS: groups refers to news groups */
251
: _("Can't validate groups. No servers responding"));
253
*expanded_group = cpystr(given_group);
259
* Now, go thru the list, making sure we can at least open each one...
261
for(server = ps_global->VAR_NNTP_SERVER;
262
server && *server && **server; server++){
264
* It's faster and easier right now just to open the stream and
265
* do our own finds than to use the current folder_exists()
268
for(ntmp = nglist; ntmp; ntmp = ntmp->next){
269
if(ntmp->found == NotInCache){
270
snprintf(ng_ref, sizeof(ng_ref), "{%.*s/nntp}#news.%.*s",
271
sizeof(ng_ref)/2 - 10, *server,
272
sizeof(ng_ref)/2 - 10, ntmp->groupname);
273
ps_global->noshow_error = 1;
274
stream = pine_mail_open(stream, ng_ref,
275
OP_SILENT|SP_USEPOOL|SP_TEMPUSE,
277
ps_global->noshow_error = 0;
279
add_newsgrp_cache(ntmp->groupname, ntmp->found = Found);
285
pine_mail_close(stream);
293
/* figure length of string for matching groups */
294
for(ntmp = nglist; ntmp; ntmp = ntmp->next){
295
if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global))
296
expanded_len += strlen(ntmp->groupname) + 2;
299
if(ntmp->found == NotInCache)
300
add_newsgrp_cache(ntmp->groupname, ntmp->found = Missing);
305
* allocate and write the allowed, and error lists...
307
p1 = *expanded_group = fs_get((expanded_len + 1) * sizeof(char));
308
if(error && num_in_error){
309
cnt_errs = num_in_error;
310
memset((void *)ng_error, 0, sizeof(ng_error));
311
snprintf(ng_error, sizeof(ng_error), "Unknown news group%s: ", plural(num_in_error));
312
ep = ng_error + strlen(ng_error);
314
for(ntmp = nglist; ntmp; ntmp = ntmp->next){
315
p2 = ntmp->groupname;
316
if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global)){
326
while(*p2 && (ep - ng_error < sizeof(ng_error)-1))
329
if(--cnt_errs > 0 && (ep - ng_error < sizeof(ng_error)-3)){
330
strncpy(ep, ", ", sizeof(ng_error)-(ep-ng_error));
338
if(error && num_in_error)
339
*error = cpystr(ng_error);
342
while(ntmp = nglist){
343
nglist = nglist->next;
344
fs_give((void **)&ntmp->groupname);
345
fs_give((void **)&ntmp);
348
return(num_in_error ? -1 : 0);
352
typedef struct ng_cache {
357
static NgCache *ng_cache_ptr;
358
#if defined(DOS) && !defined(_WINDOWS)
359
#define MAX_NGCACHE_ENTRIES 15
361
#define MAX_NGCACHE_ENTRIES 40
364
* Simple newsgroup validity cache. Opening a newsgroup to see if it
365
* exists can be very slow on a heavily loaded NNTP server, so we cache
369
chk_newsgrp_cache(char *group)
371
register NgCache *ngp;
373
for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
374
if(strcmp(group, ngp->name) == 0)
383
* Add an entry to the newsgroup validity cache.
385
* LRU entry is the one on the bottom, oldest on the top.
386
* A slot has an entry in it if name is not NULL.
389
add_newsgrp_cache(char *group, NgCacheReturns result)
391
register NgCache *ngp;
394
/* first call, initialize cache */
399
(NgCache *)fs_get((MAX_NGCACHE_ENTRIES+1)*sizeof(NgCache));
400
for(i = 0; i <= MAX_NGCACHE_ENTRIES; i++){
401
ng_cache_ptr[i].name = NULL;
402
ng_cache_ptr[i].val = NotInCache;
404
ng_cache_ptr[MAX_NGCACHE_ENTRIES].val = End;
407
if(chk_newsgrp_cache(group) == NotInCache){
408
/* find first empty slot or End */
409
for(ngp = ng_cache_ptr; ngp->name; ngp++)
413
* Cache is full, throw away top entry, move everything up,
414
* and put new entry on the bottom.
417
if(ngp->name) /* just making sure */
418
fs_give((void **)&ngp->name);
420
for(; (ngp+1)->name; ngp++){
421
ngp->name = (ngp+1)->name;
422
ngp->val = (ngp+1)->val;
425
ngp->name = cpystr(group);
430
* Move this entry from current location to last to preserve
433
for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
434
if(strcmp(group, ngp->name) == 0) /* found it */
437
save_ngp.name = ngp->name;
438
save_ngp.val = ngp->val;
439
for(; (ngp+1)->name; ngp++){
440
ngp->name = (ngp+1)->name;
441
ngp->val = (ngp+1)->val;
443
ngp->name = save_ngp.name;
444
ngp->val = save_ngp.val;
450
free_newsgrp_cache(void)
452
register NgCache *ngp;
454
for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
455
fs_give((void **)&ngp->name);
457
fs_give((void **)&ng_cache_ptr);