~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to pith/news.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if !defined(lint) && !defined(DOS)
 
2
static char rcsid[] = "$Id: news.c 155 2006-09-29 23:28:46Z hubert@u.washington.edu $";
 
3
#endif
 
4
 
 
5
/*
 
6
 * ========================================================================
 
7
 * Copyright 2006 University of Washington
 
8
 *
 
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
 
12
 *
 
13
 *     http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 * ========================================================================
 
16
 */
 
17
 
 
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"
 
25
 
 
26
 
 
27
typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;
 
28
 
 
29
 
 
30
/*
 
31
 * Internal prototypes
 
32
 */
 
33
NgCacheReturns chk_newsgrp_cache(char *);
 
34
void           add_newsgrp_cache(char *, NgCacheReturns);
 
35
 
 
36
 
 
37
/*----------------------------------------------------------------------
 
38
  Function to see if a given MAILSTREAM mailbox is in the news namespace
 
39
 
 
40
  Input: stream -- mail stream to test
 
41
 
 
42
 Result: 
 
43
  ----*/
 
44
int
 
45
ns_test(char *mailbox, char *namespace)
 
46
{
 
47
    if(mailbox){
 
48
        switch(*mailbox){
 
49
          case '#' :
 
50
            return(!struncmp(mailbox + 1, namespace, strlen(namespace)));
 
51
 
 
52
          case '{' :
 
53
          {
 
54
              NETMBX mbx;
 
55
 
 
56
              if(mail_valid_net_parse(mailbox, &mbx))
 
57
                return(ns_test(mbx.mailbox, namespace));
 
58
          }
 
59
 
 
60
          break;
 
61
 
 
62
          default :
 
63
            break;
 
64
        }
 
65
    }
 
66
 
 
67
    return(0);
 
68
}
 
69
 
 
70
 
 
71
int
 
72
news_in_folders(struct variable *var)
 
73
{
 
74
    int        i, found_news = 0;
 
75
    CONTEXT_S *tc;
 
76
 
 
77
    if(!(var && var->current_val.l))
 
78
      return(found_news);
 
79
 
 
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)
 
83
              found_news++;
 
84
            
 
85
            free_context(&tc);
 
86
        }
 
87
    }
 
88
 
 
89
    return(found_news);
 
90
}
 
91
 
 
92
 
 
93
/*----------------------------------------------------------------------
 
94
    Verify and canonicalize news groups names. 
 
95
    Called from the message composer
 
96
 
 
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
 
104
 
 
105
Returns:  0 if all is OK
 
106
         -1 if addresses weren't valid
 
107
 
 
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.
 
111
  ----*/
 
112
int
 
113
news_grouper(char *given_group, char **expanded_group, char **error,
 
114
             char **fccptr, void (*delay_warning)(void))
 
115
{
 
116
    char         ng_error[90], *p1, *p2, *name, *end, *ep, **server,
 
117
                 ng_ref[MAILTMPLEN];
 
118
    int          expanded_len = 0, num_in_error = 0, cnt_errs;
 
119
 
 
120
    MAILSTREAM  *stream = NULL;
 
121
    struct ng_list {
 
122
        char  *groupname;
 
123
        NgCacheReturns  found;
 
124
        struct ng_list *next;
 
125
    }*nglist = NULL, **ntmpp, *ntmp;
 
126
#ifdef SENDNEWS
 
127
    static int no_servers = 0;
 
128
#endif
 
129
 
 
130
    dprint((5,
 
131
        "- news_build - (%s)\n", given_group ? given_group : "nul"));
 
132
 
 
133
    if(error)
 
134
      *error = NULL;
 
135
 
 
136
    ng_ref[0] = '\0';
 
137
 
 
138
    /*------ parse given entries into a list ----*/
 
139
    ntmpp = &nglist;
 
140
    for(name = given_group; *name; name = end){
 
141
 
 
142
        /* find start of next group name */
 
143
        while(*name && (isspace((unsigned char)*name) || *name == ','))
 
144
          name++;
 
145
 
 
146
        /* find end of group name */
 
147
        end = name;
 
148
        while(*end && !isspace((unsigned char)*end) && *end != ',')
 
149
          end++;
 
150
 
 
151
        if(end != name){
 
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;
 
159
            if(!expanded_group)
 
160
              break;  /* no need to continue if just doing fcc */
 
161
        }
 
162
    }
 
163
 
 
164
    /*
 
165
     * If fcc is not set or is set to default, then replace it if
 
166
     * one of the recipient rules is in effect.
 
167
     */
 
168
    if(fccptr){
 
169
        if((ps_global->fcc_rule == FCC_RULE_RECIP ||
 
170
            ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
 
171
               (nglist && nglist->groupname)){
 
172
          if(*fccptr)
 
173
            fs_give((void **) fccptr);
 
174
 
 
175
          *fccptr = cpystr(nglist->groupname);
 
176
        }
 
177
        else if(!*fccptr) /* already default otherwise */
 
178
          *fccptr = cpystr(ps_global->VAR_DEFAULT_FCC);
 
179
    }
 
180
 
 
181
    if(!nglist){
 
182
        if(expanded_group)
 
183
          *expanded_group = cpystr("");
 
184
        return 0;
 
185
    }
 
186
 
 
187
    if(!expanded_group)
 
188
      return 0;
 
189
 
 
190
#ifdef  DEBUG
 
191
    for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
 
192
      dprint((9, "Parsed group: --[%s]--\n",
 
193
             ntmp->groupname ? ntmp->groupname : "?"));
 
194
#endif
 
195
 
 
196
    /* If we are doing validation */
 
197
    if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
 
198
        int need_to_talk_to_server = 0;
 
199
 
 
200
        /*
 
201
         * First check our cache of validated newsgroups to see if we even
 
202
         * have to open a stream.
 
203
         */
 
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++;
 
208
        }
 
209
 
 
210
        if(need_to_talk_to_server){
 
211
 
 
212
#ifdef SENDNEWS
 
213
          if(no_servers == 0)
 
214
#endif
 
215
            if(delay_warning)
 
216
              (*delay_warning)();
 
217
          
 
218
            /*
 
219
             * Build a stream to the first server that'll talk to us...
 
220
             */
 
221
            for(server = ps_global->VAR_NNTP_SERVER;
 
222
                server && *server && **server;
 
223
                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,
 
228
                                           NULL))
 
229
                  break;
 
230
            }
 
231
            if(!server || !stream){
 
232
                if(error)
 
233
#ifdef SENDNEWS
 
234
                {
 
235
                 /* don't say this over and over */
 
236
                 if(no_servers == 0){
 
237
                    if(!server || !*server || !**server)
 
238
                      no_servers++;
 
239
 
 
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"));
 
245
                 }
 
246
                }
 
247
#else
 
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"));
 
252
#endif
 
253
                *expanded_group = cpystr(given_group);
 
254
                goto done;
 
255
            }
 
256
        }
 
257
 
 
258
        /*
 
259
         * Now, go thru the list, making sure we can at least open each one...
 
260
         */
 
261
        for(server = ps_global->VAR_NNTP_SERVER;
 
262
            server && *server && **server; server++){
 
263
            /*
 
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()
 
266
             * interface...
 
267
             */
 
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,
 
276
                                          NULL);
 
277
                  ps_global->noshow_error = 0;
 
278
                  if(stream)
 
279
                    add_newsgrp_cache(ntmp->groupname, ntmp->found = Found);
 
280
                }
 
281
 
 
282
            }
 
283
 
 
284
            if(stream){
 
285
                pine_mail_close(stream);
 
286
                stream = NULL;
 
287
            }
 
288
 
 
289
        }
 
290
 
 
291
    }
 
292
 
 
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;
 
297
      else{
 
298
        num_in_error++;
 
299
        if(ntmp->found == NotInCache)
 
300
          add_newsgrp_cache(ntmp->groupname, ntmp->found = Missing);
 
301
      }
 
302
    }
 
303
 
 
304
    /*
 
305
     * allocate and write the allowed, and error lists...
 
306
     */
 
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);
 
313
    }
 
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)){
 
317
            while(*p2)
 
318
              *p1++ = *p2++;
 
319
 
 
320
            if(ntmp->next){
 
321
                *p1++ = ',';
 
322
                *p1++ = ' ';
 
323
            }
 
324
        }
 
325
        else if (error){
 
326
            while(*p2 && (ep - ng_error < sizeof(ng_error)-1))
 
327
              *ep++ = *p2++;
 
328
 
 
329
            if(--cnt_errs > 0 && (ep - ng_error < sizeof(ng_error)-3)){
 
330
                strncpy(ep, ", ", sizeof(ng_error)-(ep-ng_error));
 
331
                ep += 2;
 
332
            }
 
333
        }
 
334
    }
 
335
 
 
336
    *p1 = '\0';
 
337
 
 
338
    if(error && num_in_error)
 
339
      *error = cpystr(ng_error);
 
340
 
 
341
done:
 
342
    while(ntmp = nglist){
 
343
        nglist = nglist->next;
 
344
        fs_give((void **)&ntmp->groupname);
 
345
        fs_give((void **)&ntmp);
 
346
    }
 
347
 
 
348
    return(num_in_error ? -1 : 0);
 
349
}
 
350
 
 
351
 
 
352
typedef struct ng_cache {
 
353
    char          *name;
 
354
    NgCacheReturns val;
 
355
}NgCache;
 
356
 
 
357
static NgCache *ng_cache_ptr;
 
358
#if defined(DOS) && !defined(_WINDOWS)
 
359
#define MAX_NGCACHE_ENTRIES 15
 
360
#else
 
361
#define MAX_NGCACHE_ENTRIES 40
 
362
#endif
 
363
/*
 
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
 
366
 * the results.
 
367
 */
 
368
NgCacheReturns
 
369
chk_newsgrp_cache(char *group)
 
370
{
 
371
    register NgCache *ngp;
 
372
    
 
373
    for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
 
374
        if(strcmp(group, ngp->name) == 0)
 
375
          return(ngp->val);
 
376
    }
 
377
 
 
378
    return NotInCache;
 
379
}
 
380
 
 
381
 
 
382
/*
 
383
 * Add an entry to the newsgroup validity cache.
 
384
 *
 
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.
 
387
 */
 
388
void
 
389
add_newsgrp_cache(char *group, NgCacheReturns result)
 
390
{
 
391
    register NgCache *ngp;
 
392
    NgCache save_ngp;
 
393
 
 
394
    /* first call, initialize cache */
 
395
    if(!ng_cache_ptr){
 
396
        int i;
 
397
 
 
398
        ng_cache_ptr =
 
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;
 
403
        }
 
404
        ng_cache_ptr[MAX_NGCACHE_ENTRIES].val  = End;
 
405
    }
 
406
 
 
407
    if(chk_newsgrp_cache(group) == NotInCache){
 
408
        /* find first empty slot or End */
 
409
        for(ngp = ng_cache_ptr; ngp->name; ngp++)
 
410
          ;/* do nothing */
 
411
        if(ngp->val == End){
 
412
            /*
 
413
             * Cache is full, throw away top entry, move everything up,
 
414
             * and put new entry on the bottom.
 
415
             */
 
416
            ngp = ng_cache_ptr;
 
417
            if(ngp->name) /* just making sure */
 
418
              fs_give((void **)&ngp->name);
 
419
 
 
420
            for(; (ngp+1)->name; ngp++){
 
421
                ngp->name = (ngp+1)->name;
 
422
                ngp->val  = (ngp+1)->val;
 
423
            }
 
424
        }
 
425
        ngp->name = cpystr(group);
 
426
        ngp->val  = result;
 
427
    }
 
428
    else{
 
429
        /*
 
430
         * Move this entry from current location to last to preserve
 
431
         * LRU order.
 
432
         */
 
433
        for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
 
434
            if(strcmp(group, ngp->name) == 0) /* found it */
 
435
              break;
 
436
        }
 
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;
 
442
        }
 
443
        ngp->name = save_ngp.name;
 
444
        ngp->val  = save_ngp.val;
 
445
    }
 
446
}
 
447
 
 
448
 
 
449
void
 
450
free_newsgrp_cache(void)
 
451
{
 
452
    register NgCache *ngp;
 
453
 
 
454
    for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
 
455
      fs_give((void **)&ngp->name);
 
456
    if(ng_cache_ptr)
 
457
      fs_give((void **)&ng_cache_ptr);
 
458
}