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

« back to all changes in this revision

Viewing changes to imap/src/c-client/nntp.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
/* ========================================================================
 
2
 * Copyright 1988-2006 University of Washington
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * 
 
11
 * ========================================================================
 
12
 */
 
13
 
 
14
/*
 
15
 * Program:     Network News Transfer Protocol (NNTP) routines
 
16
 *
 
17
 * Author:      Mark Crispin
 
18
 *              Networks and Distributed Computing
 
19
 *              Computing & Communications
 
20
 *              University of Washington
 
21
 *              Administration Building, AG-44
 
22
 *              Seattle, WA  98195
 
23
 *              Internet: MRC@CAC.Washington.EDU
 
24
 *
 
25
 * Date:        10 February 1992
 
26
 * Last Edited: 6 December 2006
 
27
 */
 
28
 
 
29
 
 
30
#include <ctype.h>
 
31
#include <stdio.h>
 
32
#include "c-client.h"
 
33
#include "newsrc.h"
 
34
#include "netmsg.h"
 
35
#include "flstring.h"
 
36
 
 
37
/* Constants */
 
38
 
 
39
#define NNTPSSLPORT (long) 563  /* assigned SSL TCP contact port */
 
40
#define NNTPGREET (long) 200    /* NNTP successful greeting */
 
41
                                /* NNTP successful greeting w/o posting priv */
 
42
#define NNTPGREETNOPOST (long) 201
 
43
#define NNTPEXTOK (long) 202    /* NNTP extensions OK */
 
44
#define NNTPGOK (long) 211      /* NNTP group selection OK */
 
45
#define NNTPGLIST (long) 215    /* NNTP group list being returned */
 
46
#define NNTPARTICLE (long) 220  /* NNTP article file */
 
47
#define NNTPHEAD (long) 221     /* NNTP header text */
 
48
#define NNTPBODY (long) 222     /* NNTP body text */
 
49
#define NNTPOVER (long) 224     /* NNTP overview text */
 
50
#define NNTPOK (long) 240       /* NNTP OK code */
 
51
#define NNTPAUTHED (long) 281   /* NNTP successful authentication */
 
52
                                /* NNTP successful authentication with data */
 
53
#define NNTPAUTHEDDATA (long) 282
 
54
#define NNTPREADY (long) 340    /* NNTP ready for data */
 
55
#define NNTPWANTAUTH2 (long) 380/* NNTP authentication needed (old) */
 
56
#define NNTPWANTPASS (long) 381 /* NNTP password needed */
 
57
#define NNTPTLSSTART (long) 382 /* NNTP continue with TLS negotiation */
 
58
#define NNTPCHALLENGE (long) 383/* NNTP challenge, want response */
 
59
#define NNTPSOFTFATAL (long) 400/* NNTP soft fatal code */
 
60
#define NNTPWANTAUTH (long) 480 /* NNTP authentication needed */
 
61
#define NNTPBADCMD (long) 500   /* NNTP unrecognized command */
 
62
#define IDLETIMEOUT (long) 3    /* defined in NNTPEXT WG base draft */
 
63
 
 
64
 
 
65
/* NNTP I/O stream local data */
 
66
        
 
67
typedef struct nntp_local {
 
68
  SENDSTREAM *nntpstream;       /* NNTP stream for I/O */
 
69
  unsigned int dirty : 1;       /* disk copy of .newsrc needs updating */
 
70
  unsigned int tlsflag : 1;     /* TLS session */
 
71
  unsigned int tlssslv23 : 1;   /* TLS using SSLv23 client method */
 
72
  unsigned int notlsflag : 1;   /* TLS not used in session */
 
73
  unsigned int sslflag : 1;     /* SSL session */
 
74
  unsigned int novalidate : 1;  /* certificate not validated */
 
75
  unsigned int xover : 1;       /* supports XOVER */
 
76
  unsigned int xhdr : 1;        /* supports XHDR */
 
77
  char *name;                   /* remote newsgroup name */
 
78
  char *user;                   /* mailbox user */
 
79
  char *newsrc;                 /* newsrc file */
 
80
  char *over_fmt;               /* overview format */
 
81
  unsigned long msgno;          /* current text message number */
 
82
  FILE *txt;                    /* current text */
 
83
  unsigned long txtsize;        /* current text size */
 
84
} NNTPLOCAL;
 
85
 
 
86
 
 
87
/* Convenient access to local data */
 
88
 
 
89
#define LOCAL ((NNTPLOCAL *) stream->local)
 
90
 
 
91
 
 
92
/* Convenient access to protocol-specific data */
 
93
 
 
94
#define NNTP stream->protocol.nntp
 
95
 
 
96
 
 
97
/* Convenient access to extensions */
 
98
 
 
99
#define EXTENSION LOCAL->nntpstream->protocol.nntp.ext
 
100
 
 
101
/* Function prototypes */
 
102
 
 
103
DRIVER *nntp_valid (char *name);
 
104
DRIVER *nntp_isvalid (char *name,char *mbx);
 
105
void *nntp_parameters (long function,void *value);
 
106
void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
 
107
void nntp_list (MAILSTREAM *stream,char *ref,char *pat);
 
108
void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat);
 
109
long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat);
 
110
long nntp_subscribe (MAILSTREAM *stream,char *mailbox);
 
111
long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox);
 
112
long nntp_create (MAILSTREAM *stream,char *mailbox);
 
113
long nntp_delete (MAILSTREAM *stream,char *mailbox);
 
114
long nntp_rename (MAILSTREAM *stream,char *old,char *newname);
 
115
long nntp_status (MAILSTREAM *stream,char *mbx,long flags);
 
116
long nntp_getmap (MAILSTREAM *stream,char *name,
 
117
                  unsigned long first,unsigned long last,
 
118
                  unsigned long rnmsgs,unsigned long nmsgs,char *tmp);
 
119
MAILSTREAM *nntp_mopen (MAILSTREAM *stream);
 
120
void nntp_mclose (MAILSTREAM *stream,long options);
 
121
void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
 
122
void nntp_flags (MAILSTREAM *stream,char *sequence,long flags);
 
123
long nntp_overview (MAILSTREAM *stream,overview_t ofn);
 
124
long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt);
 
125
long nntp_over (MAILSTREAM *stream,char *sequence);
 
126
char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
 
127
                   long flags);
 
128
long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
 
129
FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
 
130
                    unsigned long *hsiz);
 
131
void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
 
132
long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
 
133
long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
 
134
                      OVERVIEW *ov);
 
135
unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
 
136
                          SORTPGM *pgm,long flags);
 
137
SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
 
138
                                 unsigned long start,unsigned long last,
 
139
                                 long flags);
 
140
THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
 
141
                         SEARCHPGM *spg,long flags);
 
142
long nntp_ping (MAILSTREAM *stream);
 
143
void nntp_check (MAILSTREAM *stream);
 
144
long nntp_expunge (MAILSTREAM *stream,char *sequence,long options);
 
145
long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
 
146
long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
 
147
 
 
148
long nntp_extensions (SENDSTREAM *stream,long flags);
 
149
long nntp_send (SENDSTREAM *stream,char *command,char *args);
 
150
long nntp_send_work (SENDSTREAM *stream,char *command,char *args);
 
151
long nntp_send_auth (SENDSTREAM *stream,long flags);
 
152
long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags);
 
153
void *nntp_challenge (void *s,unsigned long *len);
 
154
long nntp_response (void *s,char *response,unsigned long size);
 
155
long nntp_reply (SENDSTREAM *stream);
 
156
long nntp_fake (SENDSTREAM *stream,char *text);
 
157
long nntp_soutr (void *stream,char *s);
 
158
 
 
159
/* Driver dispatch used by MAIL */
 
160
 
 
161
DRIVER nntpdriver = {
 
162
  "nntp",                       /* driver name */
 
163
                                /* driver flags */
 
164
#ifdef INADEQUATE_MEMORY
 
165
  DR_LOWMEM |
 
166
#endif
 
167
  DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_CRLF|DR_RECYCLE|DR_XPOINT |
 
168
    DR_NOINTDATE|DR_NONEWMAIL|DR_HALFOPEN,
 
169
  (DRIVER *) NIL,               /* next driver */
 
170
  nntp_valid,                   /* mailbox is valid for us */
 
171
  nntp_parameters,              /* manipulate parameters */
 
172
  nntp_scan,                    /* scan mailboxes */
 
173
  nntp_list,                    /* find mailboxes */
 
174
  nntp_lsub,                    /* find subscribed mailboxes */
 
175
  nntp_subscribe,               /* subscribe to mailbox */
 
176
  nntp_unsubscribe,             /* unsubscribe from mailbox */
 
177
  nntp_create,                  /* create mailbox */
 
178
  nntp_delete,                  /* delete mailbox */
 
179
  nntp_rename,                  /* rename mailbox */
 
180
  nntp_status,                  /* status of mailbox */
 
181
  nntp_mopen,                   /* open mailbox */
 
182
  nntp_mclose,                  /* close mailbox */
 
183
  nntp_fetchfast,               /* fetch message "fast" attributes */
 
184
  nntp_flags,                   /* fetch message flags */
 
185
  nntp_overview,                /* fetch overview */
 
186
  NIL,                          /* fetch message structure */
 
187
  nntp_header,                  /* fetch message header */
 
188
  nntp_text,                    /* fetch message text */
 
189
  NIL,                          /* fetch message */
 
190
  NIL,                          /* unique identifier */
 
191
  NIL,                          /* message number from UID */
 
192
  NIL,                          /* modify flags */
 
193
  nntp_flagmsg,                 /* per-message modify flags */
 
194
  nntp_search,                  /* search for message based on criteria */
 
195
  nntp_sort,                    /* sort messages */
 
196
  nntp_thread,                  /* thread messages */
 
197
  nntp_ping,                    /* ping mailbox to see if still alive */
 
198
  nntp_check,                   /* check for new messages */
 
199
  nntp_expunge,                 /* expunge deleted messages */
 
200
  nntp_copy,                    /* copy messages to another mailbox */
 
201
  nntp_append,                  /* append string message to mailbox */
 
202
  NIL                           /* garbage collect stream */
 
203
};
 
204
 
 
205
                                /* prototype stream */
 
206
MAILSTREAM nntpproto = {&nntpdriver};
 
207
 
 
208
 
 
209
                                /* driver parameters */
 
210
static unsigned long nntp_maxlogintrials = MAXLOGINTRIALS;
 
211
static long nntp_port = 0;
 
212
static long nntp_sslport = 0;
 
213
static unsigned long nntp_range = 0;
 
214
static long nntp_hidepath = 0;
 
215
 
 
216
/* NNTP validate mailbox
 
217
 * Accepts: mailbox name
 
218
 * Returns: our driver if name is valid, NIL otherwise
 
219
 */
 
220
 
 
221
DRIVER *nntp_valid (char *name)
 
222
{
 
223
  char tmp[MAILTMPLEN];
 
224
  return nntp_isvalid (name,tmp);
 
225
}
 
226
 
 
227
 
 
228
/* NNTP validate mailbox work routine
 
229
 * Accepts: mailbox name
 
230
 *          buffer for returned mailbox name
 
231
 * Returns: our driver if name is valid, NIL otherwise
 
232
 */
 
233
 
 
234
DRIVER *nntp_isvalid (char *name,char *mbx)
 
235
{
 
236
  NETMBX mb;
 
237
  if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,nntpdriver.name)||
 
238
      mb.anoflag) return NIL;
 
239
  if (mb.mailbox[0] != '#') strcpy (mbx,mb.mailbox);
 
240
                        /* namespace format name */
 
241
  else if ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
 
242
           (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
 
243
           (mb.mailbox[5] == '.')) strcpy (mbx,mb.mailbox+6);
 
244
  else return NIL;              /* bogus name */
 
245
  return &nntpdriver;
 
246
}
 
247
 
 
248
/* News manipulate driver parameters
 
249
 * Accepts: function code
 
250
 *          function-dependent value
 
251
 * Returns: function-dependent return value
 
252
 */
 
253
 
 
254
void *nntp_parameters (long function,void *value)
 
255
{
 
256
  switch ((int) function) {
 
257
  case SET_MAXLOGINTRIALS:
 
258
    nntp_maxlogintrials = (unsigned long) value;
 
259
    break;
 
260
  case GET_MAXLOGINTRIALS:
 
261
    value = (void *) nntp_maxlogintrials;
 
262
    break;
 
263
  case SET_NNTPPORT:
 
264
    nntp_port = (long) value;
 
265
    break;
 
266
  case GET_NNTPPORT:
 
267
    value = (void *) nntp_port;
 
268
    break;
 
269
  case SET_SSLNNTPPORT:
 
270
    nntp_sslport = (long) value;
 
271
    break;
 
272
  case GET_SSLNNTPPORT:
 
273
    value = (void *) nntp_sslport;
 
274
    break;
 
275
  case SET_NNTPRANGE:
 
276
    nntp_range = (unsigned long) value;
 
277
    break;
 
278
  case GET_NNTPRANGE:
 
279
    value = (void *) nntp_range;
 
280
    break;
 
281
  case SET_NNTPHIDEPATH:
 
282
    nntp_hidepath = (long) value;
 
283
    break;
 
284
  case GET_NNTPHIDEPATH:
 
285
    value = (void *) nntp_hidepath;
 
286
    break;
 
287
  case GET_NEWSRC:
 
288
    if (value)
 
289
      value = (void *) ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->newsrc;
 
290
    break;
 
291
  case GET_IDLETIMEOUT:
 
292
    value = (void *) IDLETIMEOUT;
 
293
    break;
 
294
  case ENABLE_DEBUG:
 
295
    if (value)
 
296
      ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = T;
 
297
    break;
 
298
  case DISABLE_DEBUG:
 
299
    if (value)
 
300
      ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = NIL;
 
301
    break;
 
302
  default:
 
303
    value = NIL;                /* error case */
 
304
    break;
 
305
  }
 
306
  return value;
 
307
}
 
308
 
 
309
/* NNTP mail scan mailboxes for string
 
310
 * Accepts: mail stream
 
311
 *          reference
 
312
 *          pattern to search
 
313
 *          string to scan
 
314
 */
 
315
 
 
316
void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
 
317
{
 
318
  char tmp[MAILTMPLEN];
 
319
  if (nntp_canonicalize (ref,pat,tmp,NIL))
 
320
    mm_log ("Scan not valid for NNTP mailboxes",ERROR);
 
321
}
 
322
 
 
323
 
 
324
/* NNTP list newsgroups
 
325
 * Accepts: mail stream
 
326
 *          reference
 
327
 *          pattern to search
 
328
 */
 
329
 
 
330
void nntp_list (MAILSTREAM *stream,char *ref,char *pat)
 
331
{
 
332
  MAILSTREAM *st = stream;
 
333
  char *s,*t,*lcl,pattern[MAILTMPLEN],name[MAILTMPLEN],wildmat[MAILTMPLEN];
 
334
  int showuppers = pat[strlen (pat) - 1] == '%';
 
335
  if (!*pat) {
 
336
    if (nntp_canonicalize (ref,"*",pattern,NIL)) {
 
337
                                /* tie off name at root */
 
338
      if ((s = strchr (pattern,'}')) && (s = strchr (s+1,'.'))) *++s = '\0';
 
339
      else pattern[0] = '\0';
 
340
      mm_list (stream,'.',pattern,NIL);
 
341
    }
 
342
  }
 
343
                                /* ask server for open newsgroups */
 
344
  else if (nntp_canonicalize (ref,pat,pattern,wildmat) &&
 
345
           ((stream && LOCAL && LOCAL->nntpstream) ||
 
346
            (stream = mail_open (NIL,pattern,OP_HALFOPEN|OP_SILENT))) &&
 
347
           ((nntp_send (LOCAL->nntpstream,"LIST ACTIVE",
 
348
                        wildmat[0] ? wildmat : NIL) == NNTPGLIST) ||
 
349
            (nntp_send (LOCAL->nntpstream,"LIST",NIL) == NNTPGLIST))) {
 
350
                                /* namespace format name? */
 
351
    if (*(lcl = strchr (strcpy (name,pattern),'}') + 1) == '#') lcl += 6;
 
352
                                /* process data until we see final dot */
 
353
    while (s = net_getline (LOCAL->nntpstream->netstream)) {
 
354
      if ((*s == '.') && !s[1]){/* end of text */
 
355
        fs_give ((void **) &s);
 
356
        break;
 
357
      }
 
358
      if (t = strchr (s,' ')) { /* tie off after newsgroup name */
 
359
        *t = '\0';
 
360
        strcpy (lcl,s);         /* make full form of name */
 
361
                                /* report if match */
 
362
        if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
 
363
        else while (showuppers && (t = strrchr (lcl,'.'))) {
 
364
          *t = '\0';            /* tie off the name */
 
365
          if (pmatch_full (name,pattern,'.'))
 
366
            mm_list (stream,'.',name,LATT_NOSELECT);
 
367
        }
 
368
      }
 
369
      fs_give ((void **) &s);   /* clean up */
 
370
    }
 
371
    if (stream != st) mail_close (stream);
 
372
  }
 
373
}
 
374
 
 
375
/* NNTP list subscribed newsgroups
 
376
 * Accepts: mail stream
 
377
 *          reference
 
378
 *          pattern to search
 
379
 */
 
380
 
 
381
void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat)
 
382
{
 
383
  void *sdb = NIL;
 
384
  char *s,mbx[MAILTMPLEN];
 
385
                                /* return data from newsrc */
 
386
  if (nntp_canonicalize (ref,pat,mbx,NIL)) newsrc_lsub (stream,mbx);
 
387
  if (*pat == '{') {            /* if remote pattern, must be NNTP */
 
388
    if (!nntp_valid (pat)) return;
 
389
    ref = NIL;                  /* good NNTP pattern, punt reference */
 
390
  }
 
391
                                /* if remote reference, must be valid NNTP */
 
392
  if (ref && (*ref == '{') && !nntp_valid (ref)) return;
 
393
                                /* kludgy application of reference */
 
394
  if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
 
395
  else strcpy (mbx,pat);
 
396
 
 
397
  if (s = sm_read (&sdb)) do if (nntp_valid (s) && pmatch (s,mbx))
 
398
    mm_lsub (stream,NIL,s,NIL);
 
399
  while (s = sm_read (&sdb));   /* until no more subscriptions */
 
400
}
 
401
 
 
402
/* NNTP canonicalize newsgroup name
 
403
 * Accepts: reference
 
404
 *          pattern
 
405
 *          returned single pattern
 
406
 *          returned wildmat pattern
 
407
 * Returns: T on success, NIL on failure
 
408
 */
 
409
 
 
410
long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat)
 
411
{
 
412
  char *s;
 
413
  DRIVER *ret;
 
414
  if (ref && *ref) {            /* have a reference */
 
415
    if (!nntp_valid (ref)) return NIL;
 
416
    strcpy (pattern,ref);       /* copy reference to pattern */
 
417
                                /* # overrides mailbox field in reference */
 
418
    if (*pat == '#') strcpy (strchr (pattern,'}') + 1,pat);
 
419
                                /* pattern starts, reference ends, with . */
 
420
    else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
 
421
      strcat (pattern,pat + 1); /* append, omitting one of the period */
 
422
    else strcat (pattern,pat);  /* anything else is just appended */
 
423
  }
 
424
  else strcpy (pattern,pat);    /* just have basic name */
 
425
  if ((ret = wildmat ?          /* if valid and wildmat */
 
426
       nntp_isvalid (pattern,wildmat) : nntp_valid (pattern)) && wildmat) {
 
427
                                /* don't return wildmat if specials present */
 
428
    if (strpbrk (wildmat,",?![\\]")) wildmat[0] = '\0';
 
429
                                /* replace all % with * */
 
430
    for (s = wildmat; s = strchr (s,'%'); *s = '*');
 
431
  }
 
432
  return ret ? LONGT : NIL;
 
433
}
 
434
 
 
435
/* NNTP subscribe to mailbox
 
436
 * Accepts: mail stream
 
437
 *          mailbox to add to subscription list
 
438
 * Returns: T on success, NIL on failure
 
439
 */
 
440
 
 
441
long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
 
442
{
 
443
  char mbx[MAILTMPLEN];
 
444
  return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,':') : NIL;
 
445
}
 
446
 
 
447
 
 
448
/* NNTP unsubscribe to mailbox
 
449
 * Accepts: mail stream
 
450
 *          mailbox to delete from subscription list
 
451
 * Returns: T on success, NIL on failure
 
452
 */
 
453
 
 
454
long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
 
455
{
 
456
  char mbx[MAILTMPLEN];
 
457
  return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,'!') : NIL;
 
458
}
 
459
 
 
460
/* NNTP create mailbox
 
461
 * Accepts: mail stream
 
462
 *          mailbox name to create
 
463
 * Returns: T on success, NIL on failure
 
464
 */
 
465
 
 
466
long nntp_create (MAILSTREAM *stream,char *mailbox)
 
467
{
 
468
  return NIL;                   /* never valid for NNTP */
 
469
}
 
470
 
 
471
 
 
472
/* NNTP delete mailbox
 
473
 *          mailbox name to delete
 
474
 * Returns: T on success, NIL on failure
 
475
 */
 
476
 
 
477
long nntp_delete (MAILSTREAM *stream,char *mailbox)
 
478
{
 
479
  return NIL;                   /* never valid for NNTP */
 
480
}
 
481
 
 
482
 
 
483
/* NNTP rename mailbox
 
484
 * Accepts: mail stream
 
485
 *          old mailbox name
 
486
 *          new mailbox name
 
487
 * Returns: T on success, NIL on failure
 
488
 */
 
489
 
 
490
long nntp_rename (MAILSTREAM *stream,char *old,char *newname)
 
491
{
 
492
  return NIL;                   /* never valid for NNTP */
 
493
}
 
494
 
 
495
/* NNTP status
 
496
 * Accepts: mail stream
 
497
 *          mailbox name
 
498
 *          status flags
 
499
 * Returns: T on success, NIL on failure
 
500
 */
 
501
 
 
502
long nntp_status (MAILSTREAM *stream,char *mbx,long flags)
 
503
{
 
504
  MAILSTATUS status;
 
505
  NETMBX mb;
 
506
  unsigned long i,j,k,rnmsgs;
 
507
  long ret = NIL;
 
508
  char *s,*name,*state,tmp[MAILTMPLEN];
 
509
  char *old = (stream && !stream->halfopen) ? LOCAL->name : NIL;
 
510
  MAILSTREAM *tstream = NIL;
 
511
  if (!(mail_valid_net_parse (mbx,&mb) && !strcmp (mb.service,"nntp") &&
 
512
        *mb.mailbox &&
 
513
        ((mb.mailbox[0] != '#') ||
 
514
         ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
 
515
          (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
 
516
          (mb.mailbox[5] == '.'))))) {
 
517
    sprintf (tmp,"Invalid NNTP name %s",mbx);
 
518
    mm_log (tmp,ERROR);
 
519
    return NIL;
 
520
  }
 
521
                                /* note mailbox name */
 
522
  name = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
 
523
                                /* stream to reuse? */
 
524
  if (!(stream && LOCAL->nntpstream &&
 
525
        mail_usable_network_stream (stream,mbx)) &&
 
526
      !(tstream = stream =
 
527
        mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT|
 
528
                   ((flags & SA_MULNEWSRC) ? OP_MULNEWSRC : NIL))))
 
529
    return NIL;                 /* can't reuse or make a new one */
 
530
 
 
531
  if (nntp_send (LOCAL->nntpstream,"GROUP",name) == NNTPGOK) {
 
532
    status.flags = flags;       /* status validity flags */
 
533
    k = strtoul (LOCAL->nntpstream->reply + 4,&s,10);
 
534
    i = strtoul (s,&s,10);      /* first assigned UID */
 
535
                                /* next UID to be assigned */
 
536
    status.uidnext = (j = strtoul (s,NIL,10)) + 1;
 
537
                                /* maximum number of messages */
 
538
    rnmsgs = status.messages = (i | j) ? status.uidnext - i : 0;
 
539
    if (k > status.messages) {  /* check for absurdity */
 
540
      sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
 
541
               k,status.messages);
 
542
      mm_log (tmp,WARN);
 
543
    }
 
544
                                /* restrict article range if needed */
 
545
    if (nntp_range && (status.messages > nntp_range)) {
 
546
      i = status.uidnext - (status.messages = nntp_range);
 
547
      if (k > nntp_range) k = nntp_range;
 
548
    }
 
549
                                /* initially zero */
 
550
    status.recent = status.unseen = 0;
 
551
    if (!status.messages);      /* empty case */
 
552
                                /* use server guesstimate in simple case */
 
553
    else if (!(flags & (SA_RECENT | SA_UNSEEN))) status.messages = k;
 
554
 
 
555
                                /* have newsrc state? */
 
556
    else if (state = newsrc_state (stream,name)) {
 
557
                                /* yes, get the UID/sequence map */
 
558
      if (nntp_getmap (stream,name,i,status.uidnext - 1,rnmsgs,
 
559
                       status.messages,tmp)) {
 
560
                                /* calculate true count */
 
561
        for (status.messages = 0;
 
562
             (s = net_getline (LOCAL->nntpstream->netstream)) &&
 
563
               strcmp (s,"."); ) {
 
564
                                /* only count if in range */
 
565
          if (((k = atol (s)) >= i) && (k < status.uidnext)) {
 
566
            newsrc_check_uid (state,k,&status.recent,&status.unseen);
 
567
            status.messages++;
 
568
          }
 
569
          fs_give ((void **) &s);
 
570
        }
 
571
        if (s) fs_give ((void **) &s);
 
572
      }
 
573
                                /* assume c-client/NNTP map is entire range */
 
574
      else while (i < status.uidnext)
 
575
        newsrc_check_uid (state,i++,&status.recent,&status.unseen);
 
576
      fs_give ((void **) &state);
 
577
    }
 
578
                                /* no .newsrc state, all messages new */
 
579
    else status.recent = status.unseen = status.messages;
 
580
                                /* UID validity is a constant */
 
581
    status.uidvalidity = stream->uid_validity;
 
582
                                /* pass status to main program */
 
583
    mm_status (stream,mbx,&status);
 
584
    ret = T;                    /* succes */
 
585
  }
 
586
                                /* flush temporary stream */
 
587
  if (tstream) mail_close (tstream);
 
588
                                /* else reopen old newsgroup */
 
589
  else if (old && nntp_send (LOCAL->nntpstream,"GROUP",old) != NNTPGOK) {
 
590
    mm_log (LOCAL->nntpstream->reply,ERROR);
 
591
    stream->halfopen = T;       /* go halfopen */
 
592
  }
 
593
  return ret;                   /* success */
 
594
}
 
595
 
 
596
/* NNTP get map
 
597
 * Accepts: stream
 
598
 *          newsgroup name
 
599
 *          first UID in map range
 
600
 *          last UID in map range
 
601
 *          reported total number of messages in newsgroup
 
602
 *          calculated number of messages in range
 
603
 *          temporary buffer
 
604
 * Returns: T on success, NIL on failure
 
605
 */
 
606
 
 
607
long nntp_getmap (MAILSTREAM *stream,char *name,
 
608
                  unsigned long first,unsigned long last,
 
609
                  unsigned long rnmsgs,unsigned long nmsgs,char *tmp)
 
610
{
 
611
  short trylistgroup = NIL;
 
612
  if (rnmsgs > (nmsgs * 8))     /* small subrange? */
 
613
    trylistgroup = T;           /* yes, can try LISTGROUP if [X]HDR fails */
 
614
  else switch ((int) nntp_send (LOCAL->nntpstream,"LISTGROUP",name)) {
 
615
  case NNTPGOK:                 /* got data */
 
616
    return LONGT;
 
617
  default:                      /* else give up if server claims LISTGROUP */
 
618
    if (EXTENSION.listgroup) return NIL;
 
619
  }
 
620
                                /* build range */
 
621
  sprintf (tmp,"%lu-%lu",first,last);
 
622
  if (EXTENSION.hdr)            /* have HDR extension? */
 
623
    return (nntp_send (LOCAL->nntpstream,"HDR Date",tmp) == NNTPHEAD) ?
 
624
      LONGT : NIL;
 
625
  if (LOCAL->xhdr)              /* try the experimental extension then */
 
626
    switch ((int) nntp_send (LOCAL->nntpstream,"XHDR Date",tmp)) {
 
627
    case NNTPHEAD:              /* got an overview? */
 
628
      return LONGT;
 
629
    case NNTPBADCMD:            /* unknown command? */
 
630
      LOCAL->xhdr = NIL;        /* disable future XHDR attempts */
 
631
    }
 
632
  if (trylistgroup &&           /* no [X]HDR, maybe do LISTGROUP after all */
 
633
      (nntp_send (LOCAL->nntpstream,"LISTGROUP",name) == NNTPGOK))
 
634
    return LONGT;
 
635
  return NIL;
 
636
}
 
637
 
 
638
/* NNTP open
 
639
 * Accepts: stream to open
 
640
 * Returns: stream on success, NIL on failure
 
641
 */
 
642
 
 
643
MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
 
644
{
 
645
  unsigned long i,j,k,nmsgs,rnmsgs;
 
646
  char *s,*mbx,tmp[MAILTMPLEN];
 
647
  FILE *f;
 
648
  NETMBX mb;
 
649
  char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
 
650
  newsrcquery_t nq = (newsrcquery_t) mail_parameters (NIL,GET_NEWSRCQUERY,NIL);
 
651
  SENDSTREAM *nstream = NIL;
 
652
                                /* return prototype for OP_PROTOTYPE call */
 
653
  if (!stream) return &nntpproto;
 
654
  mail_valid_net_parse (stream->mailbox,&mb);
 
655
                                /* note mailbox anme */
 
656
  mbx = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
 
657
  if (LOCAL) {                  /* recycle stream */
 
658
    nstream = LOCAL->nntpstream;/* remember NNTP protocol stream */
 
659
    sprintf (tmp,"Reusing connection to %s",net_host (nstream->netstream));
 
660
    if (!stream->silent) mm_log (tmp,(long) NIL);
 
661
    if (stream->rdonly) mb.readonlyflag = T;
 
662
    if (LOCAL->tlsflag) mb.tlsflag = T;
 
663
    if (LOCAL->tlssslv23) mb.tlssslv23 = T;
 
664
    if (LOCAL->notlsflag) mb.notlsflag = T;
 
665
    if (LOCAL->sslflag) mb.sslflag = T;
 
666
    if (LOCAL->novalidate) mb.novalidate = T;
 
667
    if (LOCAL->nntpstream->loser) mb.loser = T;
 
668
    if (stream->secure) mb.secflag = T;
 
669
    LOCAL->nntpstream = NIL;    /* keep nntp_mclose() from punting it */
 
670
    nntp_mclose (stream,NIL);   /* do close action */
 
671
    stream->dtb = &nntpdriver;  /* reattach this driver */
 
672
  }
 
673
                                /* copy flags */
 
674
  if (mb.dbgflag) stream->debug = T;
 
675
  if (mb.readonlyflag) stream->rdonly = T;
 
676
  if (mb.secflag) stream->secure = T;
 
677
  mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
 
678
  if (!nstream) {               /* open NNTP now if not already open */
 
679
    char *hostlist[2];
 
680
    hostlist[0] = strcpy (tmp,mb.host);
 
681
    if (mb.port || nntp_port)
 
682
      sprintf (tmp + strlen (tmp),":%lu",mb.port ? mb.port : nntp_port);
 
683
    if (mb.tlsflag) strcat (tmp,"/tls");
 
684
    if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
 
685
    if (mb.notlsflag) strcat (tmp,"/notls");
 
686
    if (mb.sslflag) strcat (tmp,"/ssl");
 
687
    if (mb.novalidate) strcat (tmp,"/novalidate-cert");
 
688
    if (mb.loser) strcat (tmp,"/loser");
 
689
    if (mb.secflag) strcat (tmp,"/secure");
 
690
    if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=\"%s\"",mb.user);
 
691
    hostlist[1] = NIL;
 
692
    if (!(nstream = nntp_open (hostlist,NOP_READONLY |
 
693
                               (stream->debug ? NOP_DEBUG : NIL)))) return NIL;
 
694
  }
 
695
 
 
696
                                /* always zero messages if halfopen */
 
697
  if (stream->halfopen) i = j = k = rnmsgs = nmsgs = 0;
 
698
                                /* otherwise open the newsgroup */
 
699
  else if (nntp_send (nstream,"GROUP",mbx) == NNTPGOK) {
 
700
    k = strtoul (nstream->reply + 4,&s,10);
 
701
    i = strtoul (s,&s,10);
 
702
    stream->uid_last = j = strtoul (s,&s,10);
 
703
    rnmsgs = nmsgs = (i | j) ? 1 + j - i : 0;
 
704
    if (k > nmsgs) {            /* check for absurdity */
 
705
      sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
 
706
               k,nmsgs);
 
707
      mm_log (tmp,WARN);
 
708
    }
 
709
                                /* restrict article range if needed */
 
710
    if (nntp_range && (nmsgs > nntp_range)) i = 1 + j - (nmsgs = nntp_range);
 
711
  }
 
712
  else {                        /* no such newsgroup */
 
713
    mm_log (nstream->reply,ERROR);
 
714
    nntp_close (nstream);       /* punt stream */
 
715
    return NIL;
 
716
  }
 
717
                                /* instantiate local data */
 
718
  stream->local = memset (fs_get (sizeof (NNTPLOCAL)),0,sizeof (NNTPLOCAL));
 
719
  LOCAL->nntpstream = nstream;
 
720
                                /* save state for future recycling */
 
721
  if (mb.tlsflag) LOCAL->tlsflag = T;
 
722
  if (mb.tlssslv23) LOCAL->tlssslv23 = T;
 
723
  if (mb.notlsflag) LOCAL->notlsflag = T;
 
724
  if (mb.sslflag) LOCAL->sslflag = T;
 
725
  if (mb.novalidate) LOCAL->novalidate = T;
 
726
  if (mb.loser) LOCAL->nntpstream->loser = T;
 
727
                                /* assume present until proven otherwise */
 
728
  LOCAL->xhdr = LOCAL->xover = T;
 
729
  LOCAL->name = cpystr (mbx);   /* copy newsgroup name */
 
730
  if (stream->mulnewsrc) {      /* want to use multiple .newsrc files? */
 
731
    strcpy (tmp,newsrc);
 
732
    s = tmp + strlen (tmp);     /* end of string */
 
733
    *s++ = '-';                 /* hyphen delimiter and host */
 
734
    lcase (strcpy (s,(long) mail_parameters (NIL,GET_NEWSRCCANONHOST,NIL) ?
 
735
                   net_host (nstream->netstream) : mb.host));
 
736
    LOCAL->newsrc = cpystr (nq ? (*nq) (stream,tmp,newsrc) : tmp);
 
737
  }
 
738
  else LOCAL->newsrc = cpystr (newsrc);
 
739
  if (mb.user[0]) LOCAL->user = cpystr (mb.user);
 
740
  stream->sequence++;           /* bump sequence number */
 
741
  stream->rdonly = stream->perm_deleted = T;
 
742
                                /* UIDs are always valid */
 
743
  stream->uid_validity = 0xbeefface;
 
744
  sprintf (tmp,"{%s:%lu/nntp",(int) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
 
745
           net_host (nstream->netstream) : mb.host,
 
746
           net_port (nstream->netstream));
 
747
  if (LOCAL->tlsflag) strcat (tmp,"/tls");
 
748
  if (LOCAL->tlssslv23) strcat (tmp,"/tls-sslv23");
 
749
  if (LOCAL->notlsflag) strcat (tmp,"/notls");
 
750
  if (LOCAL->sslflag) strcat (tmp,"/ssl");
 
751
  if (LOCAL->novalidate) strcat (tmp,"/novalidate-cert");
 
752
  if (LOCAL->nntpstream->loser) strcat (tmp,"/loser");
 
753
  if (stream->secure) strcat (tmp,"/secure");
 
754
  if (stream->rdonly) strcat (tmp,"/readonly");
 
755
  if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",LOCAL->user);
 
756
  if (stream->halfopen) strcat (tmp,"}<no_mailbox>");
 
757
  else sprintf (tmp + strlen (tmp),"}#news.%s",mbx);
 
758
  fs_give ((void **) &stream->mailbox);
 
759
  stream->mailbox = cpystr (tmp);
 
760
 
 
761
  if (EXTENSION.over &&         /* get overview format if have OVER */
 
762
      (nntp_send (LOCAL->nntpstream,"LIST","OVERVIEW.FMT") == NNTPGLIST) &&
 
763
      (f = netmsg_slurp (LOCAL->nntpstream->netstream,&k,NIL))) {
 
764
    fread (LOCAL->over_fmt = (char *) fs_get ((size_t) k + 3),
 
765
           (size_t) 1,(size_t) k,f);
 
766
    LOCAL->over_fmt[k] = '\0';
 
767
    fclose (f);                 /* flush temp file */
 
768
  }
 
769
  if (nmsgs) {                  /* if any messages exist */
 
770
    short silent = stream->silent;
 
771
    stream->silent = T;         /* don't notify main program yet */
 
772
    mail_exists (stream,nmsgs); /* silently set the cache to the guesstimate */
 
773
                                /* get UID/sequence map, nuke holes */
 
774
    if (nntp_getmap (stream,mbx,i,j,rnmsgs,nmsgs,tmp)) {
 
775
      for (nmsgs = 0;           /* calculate true count */
 
776
           (s = net_getline (nstream->netstream)) && strcmp (s,"."); ) {
 
777
        if ((k = atol (s)) > j){/* discard too high article numbers */
 
778
          sprintf (tmp,"NNTP SERVER BUG (out of range article ID): %lu > %lu",
 
779
                   k,j);
 
780
          mm_notify (stream,tmp,NIL);
 
781
          stream->unhealthy = T;
 
782
        }
 
783
        else if (k >= i) {      /* silently ignore too-low article numbers */
 
784
                                /* guard against server returning extra msgs */
 
785
          if (nmsgs == stream->nmsgs) mail_exists (stream,nmsgs+1);
 
786
                                /* create elt for this message, set UID */
 
787
          mail_elt (stream,++nmsgs)->private.uid = k;
 
788
        }
 
789
        fs_give ((void **) &s);
 
790
      }
 
791
      if (s) fs_give ((void **) &s);
 
792
    }
 
793
                                /* assume c-client/NNTP map is entire range */
 
794
    else for (k = 1; k <= nmsgs; k++) mail_elt (stream,k)->private.uid = i++;
 
795
    stream->unhealthy = NIL;    /* set healthy */
 
796
    stream->nmsgs = 0;          /* whack it back down */
 
797
    stream->silent = silent;    /* restore old silent setting */
 
798
    mail_exists (stream,nmsgs); /* notify upper level that messages exist */
 
799
                                /* read .newsrc entries */
 
800
    mail_recent (stream,newsrc_read (mbx,stream));
 
801
  }
 
802
  else {                        /* empty newsgroup or halfopen */
 
803
    if (!(stream->silent || stream->halfopen)) {
 
804
      sprintf (tmp,"Newsgroup %s is empty",mbx);
 
805
      mm_log (tmp,WARN);
 
806
    }
 
807
    mail_exists (stream,(long) 0);
 
808
    mail_recent (stream,(long) 0);
 
809
  }
 
810
  return stream;                /* return stream to caller */
 
811
}
 
812
 
 
813
/* NNTP close
 
814
 * Accepts: MAIL stream
 
815
 *          option flags
 
816
 */
 
817
 
 
818
void nntp_mclose (MAILSTREAM *stream,long options)
 
819
{
 
820
  unsigned long i;
 
821
  MESSAGECACHE *elt;
 
822
  if (LOCAL) {                  /* only if a file is open */
 
823
    nntp_check (stream);        /* dump final checkpoint */
 
824
    if (LOCAL->over_fmt) fs_give ((void **) &LOCAL->over_fmt);
 
825
    if (LOCAL->name) fs_give ((void **) &LOCAL->name);
 
826
    if (LOCAL->user) fs_give ((void **) &LOCAL->user);
 
827
    if (LOCAL->newsrc) fs_give ((void **) &LOCAL->newsrc);
 
828
    if (LOCAL->txt) fclose (LOCAL->txt);
 
829
                                /* close NNTP connection */
 
830
    if (LOCAL->nntpstream) nntp_close (LOCAL->nntpstream);
 
831
    for (i = 1; i <= stream->nmsgs; i++)
 
832
      if ((elt = mail_elt (stream,i))->private.spare.ptr)
 
833
        fs_give ((void **) &elt->private.spare.ptr);
 
834
                                /* nuke the local data */
 
835
    fs_give ((void **) &stream->local);
 
836
    stream->dtb = NIL;          /* log out the DTB */
 
837
  }
 
838
}
 
839
 
 
840
/* NNTP fetch fast information
 
841
 * Accepts: MAIL stream
 
842
 *          sequence
 
843
 *          option flags
 
844
 * This is ugly and slow
 
845
 */
 
846
 
 
847
void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
 
848
{
 
849
  unsigned long i;
 
850
  MESSAGECACHE *elt;
 
851
                                /* get sequence */
 
852
  if (stream && LOCAL && ((flags & FT_UID) ?
 
853
                          mail_uid_sequence (stream,sequence) :
 
854
                          mail_sequence (stream,sequence)))
 
855
    for (i = 1; i <= stream->nmsgs; i++) {
 
856
      if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) &&
 
857
          !(elt->day && elt->rfc822_size)) {
 
858
        ENVELOPE **env = NIL;
 
859
        ENVELOPE *e = NIL;
 
860
        if (!stream->scache) env = &elt->private.msg.env;
 
861
        else if (stream->msgno == i) env = &stream->env;
 
862
        else env = &e;
 
863
        if (!*env || !elt->rfc822_size) {
 
864
          STRING bs;
 
865
          unsigned long hs;
 
866
          char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
 
867
                                /* need to make an envelope? */
 
868
          if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
 
869
                                       stream->dtb->flags);
 
870
                                /* need message size too, ugh */
 
871
          if (!elt->rfc822_size) {
 
872
            (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
 
873
            elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
 
874
          }
 
875
        }
 
876
                                /* if need date, have date in envelope? */
 
877
        if (!elt->day && *env && (*env)->date)
 
878
          mail_parse_date (elt,(*env)->date);
 
879
                                /* sigh, fill in bogus default */
 
880
        if (!elt->day) elt->day = elt->month = 1;
 
881
        mail_free_envelope (&e);
 
882
      }
 
883
    }
 
884
}
 
885
 
 
886
/* NNTP fetch flags
 
887
 * Accepts: MAIL stream
 
888
 *          sequence
 
889
 *          option flags
 
890
 */
 
891
 
 
892
void nntp_flags (MAILSTREAM *stream,char *sequence,long flags)
 
893
{
 
894
  unsigned long i;
 
895
  if ((flags & FT_UID) ?        /* validate all elts */
 
896
      mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
 
897
    for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
 
898
}
 
899
 
 
900
/* NNTP fetch overview
 
901
 * Accepts: MAIL stream, sequence bits set
 
902
 *          overview return function
 
903
 * Returns: T if successful, NIL otherwise
 
904
 */
 
905
 
 
906
long nntp_overview (MAILSTREAM *stream,overview_t ofn)
 
907
{
 
908
  unsigned long i,j,k,uid;
 
909
  char c,*s,*t,*v,tmp[MAILTMPLEN];
 
910
  MESSAGECACHE *elt;
 
911
  OVERVIEW ov;
 
912
  if (!LOCAL->nntpstream->netstream) return NIL;
 
913
                                /* scan sequence to load cache */
 
914
  for (i = 1; i <= stream->nmsgs; i++)
 
915
                                /* have cached overview yet? */
 
916
    if ((elt = mail_elt (stream,i))->sequence && !elt->private.spare.ptr) {
 
917
      for (j = i + 1;           /* no, find end of cache gap range */
 
918
           (j <= stream->nmsgs) && (elt = mail_elt (stream,j))->sequence &&
 
919
           !elt->private.spare.ptr; j++);
 
920
                                /* make NNTP range */
 
921
      sprintf (tmp,(i == (j - 1)) ? "%lu" : "%lu-%lu",mail_uid (stream,i),
 
922
               mail_uid (stream,j - 1));
 
923
      i = j;                    /* advance beyond gap */
 
924
                                /* ask server for overview data to cache */
 
925
      if (nntp_over (stream,tmp)) {
 
926
        while ((s = net_getline (LOCAL->nntpstream->netstream)) &&
 
927
               strcmp (s,".")) {
 
928
                                /* death to embedded newlines */
 
929
          for (t = v = s; c = *v++;)
 
930
            if ((c != '\012') && (c != '\015')) *t++ = c;
 
931
          *t++ = '\0';          /* tie off string in case it was shortened */
 
932
                                /* cache the overview if found its sequence */
 
933
          if ((uid = atol (s)) && (k = mail_msgno (stream,uid)) &&
 
934
              (t = strchr (s,'\t'))) {
 
935
            if ((elt = mail_elt (stream,k))->private.spare.ptr)
 
936
              fs_give ((void **) &elt->private.spare.ptr);
 
937
            elt->private.spare.ptr = cpystr (t + 1);
 
938
          }
 
939
          else {                /* shouldn't happen, snarl if it does */
 
940
            sprintf (tmp,"Server returned data for unknown UID %lu",uid);
 
941
            mm_notify (stream,tmp,WARN);
 
942
            stream->unhealthy = T;
 
943
          }
 
944
                                /* flush the overview */
 
945
          fs_give ((void **) &s);
 
946
        }
 
947
        stream->unhealthy = NIL;/* set healthy */
 
948
                                /* flush the terminating dot */
 
949
        if (s) fs_give ((void **) &s);
 
950
      }
 
951
      else i = stream->nmsgs;   /* OVER failed, punt cache load */
 
952
    }
 
953
 
 
954
                                /* now scan sequence to return overviews */
 
955
  if (ofn) for (i = 1; i <= stream->nmsgs; i++)
 
956
    if ((elt = mail_elt (stream,i))->sequence) {
 
957
      uid = mail_uid (stream,i);/* UID for this message */
 
958
                                /* parse cached overview */
 
959
      if (nntp_parse_overview (&ov,s = (char *) elt->private.spare.ptr,elt))
 
960
        (*ofn) (stream,uid,&ov,i);
 
961
      else {                    /* parse failed */
 
962
        (*ofn) (stream,uid,NIL,i);
 
963
        if (s && *s) {          /* unusable cached entry? */
 
964
          sprintf (tmp,"Unable to parse overview for UID %lu: %.500s",uid,s);
 
965
          mm_notify (stream,tmp,WARN);
 
966
          stream->unhealthy = T;
 
967
                                /* erase it from the cache */
 
968
          fs_give ((void **) &s);
 
969
        }
 
970
        stream->unhealthy = NIL;/* set healthy */
 
971
                                /* insert empty cached text as necessary */
 
972
        if (!s) elt->private.spare.ptr = cpystr ("");
 
973
      }
 
974
                                /* clean up overview data */
 
975
      if (ov.from) mail_free_address (&ov.from);
 
976
      if (ov.subject) fs_give ((void **) &ov.subject);
 
977
    }
 
978
  return T;
 
979
}
 
980
 
 
981
/* Send OVER to NNTP server
 
982
 * Accepts: mail stream
 
983
 *          sequence to send
 
984
 * Returns: T if success and overviews will follow, else NIL
 
985
 */
 
986
 
 
987
long nntp_over (MAILSTREAM *stream,char *sequence)
 
988
{
 
989
  unsigned char *s;
 
990
                                /* test for Netscape Collabra server */
 
991
  if (EXTENSION.over && LOCAL->xover &&
 
992
      nntp_send (LOCAL->nntpstream,"OVER","0") == NNTPOVER) {
 
993
    /* "Netscape-Collabra/3.52 03615 NNTP" responds to the OVER command with
 
994
     * a bogus "Subject:From:Date:Bytes:Lines" response followed by overviews
 
995
     * which lack the Message-ID and References:.  This violates the draft
 
996
     * NNTP specification (draft-ietf-nntpext-base-18.txt as of this writing).
 
997
     * XOVER works fine.
 
998
     */
 
999
    while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
 
1000
      if (!isdigit (*s)) {      /* is it that fetid piece of reptile dung? */
 
1001
        EXTENSION.over = NIL;   /* sure smells like it */
 
1002
        mm_log ("Working around Netscape Collabra bug",WARN);
 
1003
      }
 
1004
      fs_give ((void **) &s);   /* flush the overview */
 
1005
    }
 
1006
    if (s) fs_give ((void **) &s);
 
1007
                                /* don't do this test again */
 
1008
    if (EXTENSION.over) LOCAL->xover = NIL;
 
1009
  }
 
1010
  if (EXTENSION.over)           /* have OVER extension? */
 
1011
    return (nntp_send (LOCAL->nntpstream,"OVER",sequence) == NNTPOVER) ?
 
1012
      LONGT : NIL;
 
1013
  if (LOCAL->xover)             /* try the experiment extension then */
 
1014
    switch ((int) nntp_send (LOCAL->nntpstream,"XOVER",sequence)) {
 
1015
    case NNTPOVER:              /* got an overview? */
 
1016
      return LONGT;
 
1017
    case NNTPBADCMD:            /* unknown command? */
 
1018
      LOCAL->xover = NIL;       /* disable future XOVER attempts */
 
1019
    }
 
1020
  return NIL;
 
1021
}
 
1022
 
 
1023
/* Parse OVERVIEW struct from cached NNTP OVER response
 
1024
 * Accepts: struct to load
 
1025
 *          cached OVER response
 
1026
 *          internaldate
 
1027
 * Returns: T if success, NIL if fail
 
1028
 */
 
1029
 
 
1030
long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt)
 
1031
{
 
1032
  char *t;
 
1033
                                /* nothing in overview yet */
 
1034
  memset ((void *) ov,0,sizeof (OVERVIEW));
 
1035
                                /* no cached data */
 
1036
  if (!(text && *text)) return NIL;
 
1037
  ov->subject = cpystr (text);  /* make hackable copy of overview */
 
1038
                                /* find end of Subject */
 
1039
  if (t = strchr (ov->subject,'\t')) {
 
1040
    *t++ = '\0';                /* tie off Subject, point to From */
 
1041
                                /* find end of From */
 
1042
    if (ov->date = strchr (t,'\t')) {
 
1043
      *ov->date++ = '\0';       /* tie off From, point to Date */
 
1044
                                /* load internaldate too */
 
1045
      if (!elt->day) mail_parse_date (elt,ov->date);
 
1046
                                /* parse From */
 
1047
      rfc822_parse_adrlist (&ov->from,t,BADHOST);
 
1048
                                /* find end of Date */
 
1049
      if (ov->message_id = strchr (ov->date,'\t')) {
 
1050
                                /* tie off Date, point to Message-ID */
 
1051
        *ov->message_id++ = '\0';
 
1052
                                /* find end of Message-ID */
 
1053
        if (ov->references = strchr (ov->message_id,'\t')) {
 
1054
                                /* tie off Message-ID, point to References */
 
1055
          *ov->references++ = '\0';
 
1056
                                /* fine end of References */
 
1057
          if (t = strchr (ov->references,'\t')) {
 
1058
            *t++ = '\0';        /* tie off References, point to octet size */
 
1059
                                /* parse size of message in octets */
 
1060
            ov->optional.octets = atol (t);
 
1061
                                /* find end of size */
 
1062
            if (t = strchr (t,'\t')) {
 
1063
                                /* parse size of message in lines */
 
1064
              ov->optional.lines = atol (++t);
 
1065
                                /* find Xref */
 
1066
              if (ov->optional.xref = strchr (t,'\t'))
 
1067
                *ov->optional.xref++ = '\0';
 
1068
            }
 
1069
          }
 
1070
        }
 
1071
      }
 
1072
    }
 
1073
  }
 
1074
  return ov->references ? T : NIL;
 
1075
}
 
1076
 
 
1077
/* NNTP fetch header as text
 
1078
 * Accepts: mail stream
 
1079
 *          message number
 
1080
 *          pointer to return size
 
1081
 *          flags
 
1082
 * Returns: header text
 
1083
 */
 
1084
 
 
1085
char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
 
1086
                   long flags)
 
1087
{
 
1088
  char tmp[MAILTMPLEN];
 
1089
  MESSAGECACHE *elt;
 
1090
  FILE *f;
 
1091
  *size = 0;
 
1092
  if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
 
1093
                                /* have header text? */
 
1094
  if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
 
1095
    sprintf (tmp,"%lu",mail_uid (stream,msgno));
 
1096
                                /* get header text */
 
1097
    switch (nntp_send (LOCAL->nntpstream,"HEAD",tmp)) {
 
1098
    case NNTPHEAD:
 
1099
      if (f = netmsg_slurp (LOCAL->nntpstream->netstream,size,NIL)) {
 
1100
        fread (elt->private.msg.header.text.data =
 
1101
               (unsigned char *) fs_get ((size_t) *size + 3),
 
1102
               (size_t) 1,(size_t) *size,f);
 
1103
        fclose (f);             /* flush temp file */
 
1104
                                /* tie off header with extra CRLF and NUL */
 
1105
        elt->private.msg.header.text.data[*size] = '\015';
 
1106
        elt->private.msg.header.text.data[++*size] = '\012';
 
1107
        elt->private.msg.header.text.data[++*size] = '\0';
 
1108
        elt->private.msg.header.text.size = *size;
 
1109
        elt->valid = T;         /* make elt valid now */
 
1110
        break;
 
1111
      }
 
1112
                                /* fall into default case */
 
1113
    default:                    /* failed, mark as deleted and empty */
 
1114
      elt->valid = elt->deleted = T;
 
1115
    case NNTPSOFTFATAL:         /* don't mark deleted if stream dead */
 
1116
      *size = elt->private.msg.header.text.size = 0;
 
1117
      break;
 
1118
    }
 
1119
  }
 
1120
                                /* just return size of text */
 
1121
  else *size = elt->private.msg.header.text.size;
 
1122
  return elt->private.msg.header.text.data ?
 
1123
    (char *) elt->private.msg.header.text.data : "";
 
1124
}
 
1125
 
 
1126
/* NNTP fetch body
 
1127
 * Accepts: mail stream
 
1128
 *          message number
 
1129
 *          pointer to stringstruct to initialize
 
1130
 *          flags
 
1131
 * Returns: T if successful, else NIL
 
1132
 */
 
1133
 
 
1134
long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
 
1135
{
 
1136
  char tmp[MAILTMPLEN];
 
1137
  MESSAGECACHE *elt;
 
1138
  INIT (bs,mail_string,(void *) "",0);
 
1139
  if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
 
1140
  elt = mail_elt (stream,msgno);
 
1141
                                /* different message, flush cache */
 
1142
  if (LOCAL->txt && (LOCAL->msgno != msgno)) {
 
1143
    fclose (LOCAL->txt);
 
1144
    LOCAL->txt = NIL;
 
1145
  }
 
1146
  LOCAL->msgno = msgno;         /* note cached message */
 
1147
  if (!LOCAL->txt) {            /* have file for this message? */
 
1148
    sprintf (tmp,"%lu",elt->private.uid);
 
1149
    switch (nntp_send (LOCAL->nntpstream,"BODY",tmp)) {
 
1150
    case NNTPBODY:
 
1151
      if (LOCAL->txt = netmsg_slurp (LOCAL->nntpstream->netstream,
 
1152
                                     &LOCAL->txtsize,NIL)) break;
 
1153
                                /* fall into default case */
 
1154
    default:                    /* failed, mark as deleted */
 
1155
      elt->deleted = T;
 
1156
    case NNTPSOFTFATAL:         /* don't mark deleted if stream dead */
 
1157
      return NIL;
 
1158
    }
 
1159
  }
 
1160
  if (!(flags & FT_PEEK)) {     /* mark seen if needed */
 
1161
    elt->seen = T;
 
1162
    mm_flags (stream,elt->msgno);
 
1163
  }
 
1164
  INIT (bs,file_string,(void *) LOCAL->txt,LOCAL->txtsize);
 
1165
  return T;
 
1166
}
 
1167
 
 
1168
/* NNTP fetch article from message ID (for news: URL support)
 
1169
 * Accepts: mail stream
 
1170
 *          message ID
 
1171
 *          pointer to return total message size
 
1172
 *          pointer to return file size
 
1173
 * Returns: FILE * to message if successful, else NIL
 
1174
 */
 
1175
 
 
1176
FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
 
1177
                    unsigned long *hsiz)
 
1178
{
 
1179
  return (nntp_send (LOCAL->nntpstream,"ARTICLE",msgid) == NNTPARTICLE) ?
 
1180
    netmsg_slurp (LOCAL->nntpstream->netstream,size,hsiz) : NIL;
 
1181
}
 
1182
 
 
1183
 
 
1184
/* NNTP per-message modify flag
 
1185
 * Accepts: MAIL stream
 
1186
 *          message cache element
 
1187
 */
 
1188
 
 
1189
void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
 
1190
{
 
1191
  if (!LOCAL->dirty) {          /* only bother checking if not dirty yet */
 
1192
    if (elt->valid) {           /* if done, see if deleted changed */
 
1193
      if (elt->sequence != elt->deleted) LOCAL->dirty = T;
 
1194
      elt->sequence = T;        /* leave the sequence set */
 
1195
    }
 
1196
                                /* note current setting of deleted flag */
 
1197
    else elt->sequence = elt->deleted;
 
1198
  }
 
1199
}
 
1200
 
 
1201
/* NNTP search messages
 
1202
 * Accepts: mail stream
 
1203
 *          character set
 
1204
 *          search program
 
1205
 *          option flags
 
1206
 * Returns: T on success, NIL on failure
 
1207
 */
 
1208
 
 
1209
long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
 
1210
{
 
1211
  unsigned long i;
 
1212
  MESSAGECACHE *elt;
 
1213
  OVERVIEW ov;
 
1214
  if (charset && *charset &&    /* convert if charset not US-ASCII or UTF-8 */
 
1215
      !(((charset[0] == 'U') || (charset[0] == 'u')) &&
 
1216
        ((((charset[1] == 'S') || (charset[1] == 's')) &&
 
1217
          (charset[2] == '-') &&
 
1218
          ((charset[3] == 'A') || (charset[3] == 'a')) &&
 
1219
          ((charset[4] == 'S') || (charset[4] == 's')) &&
 
1220
          ((charset[5] == 'C') || (charset[5] == 'c')) &&
 
1221
          ((charset[6] == 'I') || (charset[6] == 'i')) &&
 
1222
          ((charset[7] == 'I') || (charset[7] == 'i')) && !charset[8]) ||
 
1223
         (((charset[1] == 'T') || (charset[1] == 't')) &&
 
1224
          ((charset[2] == 'F') || (charset[2] == 'f')) &&
 
1225
          (charset[3] == '-') && (charset[4] == '8') && !charset[5])))) {
 
1226
    if (utf8_text (NIL,charset,NIL,T)) utf8_searchpgm (pgm,charset);
 
1227
    else return NIL;            /* charset unknown */
 
1228
  }
 
1229
  if (flags & SO_OVERVIEW) {    /* only if specified to use overview */
 
1230
                                /* identify messages that will be searched */
 
1231
    for (i = 1; i <= stream->nmsgs; ++i)
 
1232
      mail_elt (stream,i)->sequence = nntp_search_msg (stream,i,pgm,NIL);
 
1233
    nntp_overview (stream,NIL); /* load the overview cache */
 
1234
  }
 
1235
                                /* init in case no overview at cleanup */
 
1236
  memset ((void *) &ov,0,sizeof (OVERVIEW));
 
1237
                                /* otherwise do default search */
 
1238
  for (i = 1; i <= stream->nmsgs; ++i) {
 
1239
    if (((flags & SO_OVERVIEW) && ((elt = mail_elt (stream,i))->sequence) &&
 
1240
         nntp_parse_overview (&ov,(char *) elt->private.spare.ptr,elt)) ?
 
1241
        nntp_search_msg (stream,i,pgm,&ov) :
 
1242
        mail_search_msg (stream,i,NIL,pgm)) {
 
1243
      if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
 
1244
      else {                    /* mark as searched, notify mail program */
 
1245
        mail_elt (stream,i)->searched = T;
 
1246
        if (!stream->silent) mm_searched (stream,i);
 
1247
      }
 
1248
    }
 
1249
                                /* clean up overview data */
 
1250
    if (ov.from) mail_free_address (&ov.from);
 
1251
    if (ov.subject) fs_give ((void **) &ov.subject);
 
1252
  }
 
1253
  return LONGT;
 
1254
}
 
1255
 
 
1256
/* NNTP search message
 
1257
 * Accepts: MAIL stream
 
1258
 *          message number
 
1259
 *          search program
 
1260
 *          overview to search (NIL means preliminary pass)
 
1261
 * Returns: T if found, NIL otherwise
 
1262
 */
 
1263
 
 
1264
long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
 
1265
                      OVERVIEW *ov)
 
1266
{
 
1267
  unsigned short d;
 
1268
  MESSAGECACHE *elt = mail_elt (stream,msgno);
 
1269
  SEARCHHEADER *hdr;
 
1270
  SEARCHOR *or;
 
1271
  SEARCHPGMLIST *not;
 
1272
  if (pgm->msgno || pgm->uid) { /* message set searches */
 
1273
    SEARCHSET *set;
 
1274
                                /* message sequences */
 
1275
    if (set = pgm->msgno) {     /* must be inside this sequence */
 
1276
      while (set) {             /* run down until find matching range */
 
1277
        if (set->last ? ((msgno < set->first) || (msgno > set->last)) :
 
1278
            msgno != set->first) set = set->next;
 
1279
        else break;
 
1280
      }
 
1281
      if (!set) return NIL;     /* not found within sequence */
 
1282
    }
 
1283
    if (set = pgm->uid) {       /* must be inside this sequence */
 
1284
      unsigned long uid = mail_uid (stream,msgno);
 
1285
      while (set) {             /* run down until find matching range */
 
1286
        if (set->last ? ((uid < set->first) || (uid > set->last)) :
 
1287
            uid != set->first) set = set->next;
 
1288
        else break;
 
1289
      }
 
1290
      if (!set) return NIL;     /* not found within sequence */
 
1291
    }
 
1292
  }
 
1293
 
 
1294
  /* Fast data searches */
 
1295
                                /* message flags */
 
1296
  if ((pgm->answered && !elt->answered) ||
 
1297
      (pgm->unanswered && elt->answered) ||
 
1298
      (pgm->deleted && !elt->deleted) ||
 
1299
      (pgm->undeleted && elt->deleted) ||
 
1300
      (pgm->draft && !elt->draft) ||
 
1301
      (pgm->undraft && elt->draft) ||
 
1302
      (pgm->flagged && !elt->flagged) ||
 
1303
      (pgm->unflagged && elt->flagged) ||
 
1304
      (pgm->recent && !elt->recent) ||
 
1305
      (pgm->old && elt->recent) ||
 
1306
      (pgm->seen && !elt->seen) ||
 
1307
      (pgm->unseen && elt->seen)) return NIL;
 
1308
                                /* keywords */
 
1309
  if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
 
1310
      (pgm->unkeyword && mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
 
1311
    return NIL;
 
1312
  if (ov) {                     /* only do this if real searching */
 
1313
    MESSAGECACHE delt;
 
1314
                                /* size ranges */
 
1315
    if ((pgm->larger && (ov->optional.octets <= pgm->larger)) ||
 
1316
        (pgm->smaller && (ov->optional.octets >= pgm->smaller))) return NIL;
 
1317
                                /* date ranges */
 
1318
    if ((pgm->sentbefore || pgm->senton || pgm->sentsince ||
 
1319
         (pgm->before || pgm->on || pgm->since)) &&
 
1320
        (!mail_parse_date (&delt,ov->date) ||
 
1321
         !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
 
1322
         (pgm->sentbefore && (d >= pgm->sentbefore)) ||
 
1323
         (pgm->senton && (d != pgm->senton)) ||
 
1324
         (pgm->sentsince && (d < pgm->sentsince)) ||
 
1325
         (pgm->before && (d >= pgm->before)) ||
 
1326
         (pgm->on && (d != pgm->on)) ||
 
1327
         (pgm->since && (d < pgm->since)))) return NIL;
 
1328
    if ((pgm->from && !mail_search_addr (ov->from,pgm->from)) ||
 
1329
        (pgm->subject && !mail_search_header_text (ov->subject,pgm->subject))||
 
1330
        (pgm->message_id &&
 
1331
         !mail_search_header_text (ov->message_id,pgm->message_id)) ||
 
1332
        (pgm->references &&
 
1333
         !mail_search_header_text (ov->references,pgm->references)))
 
1334
      return NIL;
 
1335
 
 
1336
 
 
1337
                                /* envelope searches */
 
1338
    if (pgm->bcc || pgm->cc || pgm->to || pgm->return_path || pgm->sender ||
 
1339
        pgm->reply_to || pgm->in_reply_to || pgm->newsgroups ||
 
1340
        pgm->followup_to) {
 
1341
      ENVELOPE *env = mail_fetchenvelope (stream,msgno);
 
1342
      if (!env) return NIL;     /* no envelope obtained */
 
1343
                                /* search headers */
 
1344
      if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
 
1345
          (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
 
1346
          (pgm->to && !mail_search_addr (env->to,pgm->to)))
 
1347
        return NIL;
 
1348
      /* These criteria are not supported by IMAP and have to be emulated */
 
1349
      if ((pgm->return_path &&
 
1350
           !mail_search_addr (env->return_path,pgm->return_path)) ||
 
1351
          (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
 
1352
          (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
 
1353
          (pgm->in_reply_to &&
 
1354
           !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
 
1355
          (pgm->newsgroups &&
 
1356
           !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
 
1357
          (pgm->followup_to &&
 
1358
           !mail_search_header_text (env->followup_to,pgm->followup_to)))
 
1359
        return NIL;
 
1360
    }
 
1361
 
 
1362
                                /* search header lines */
 
1363
    for (hdr = pgm->header; hdr; hdr = hdr->next) {
 
1364
      char *t,*e,*v;
 
1365
      SIZEDTEXT s;
 
1366
      STRINGLIST sth,stc;
 
1367
      sth.next = stc.next = NIL;/* only one at a time */
 
1368
      sth.text.data = hdr->line.data;
 
1369
      sth.text.size = hdr->line.size;
 
1370
                                /* get the header text */
 
1371
      if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
 
1372
                                  FT_INTERNAL | FT_PEEK)) && strchr (t,':')) {
 
1373
        if (hdr->text.size) {   /* anything matches empty search string */
 
1374
                                /* non-empty, copy field data */
 
1375
          s.data = (unsigned char *) fs_get (s.size + 1);
 
1376
                                /* for each line */
 
1377
          for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
 
1378
          default:              /* non-continuation, skip leading field name */
 
1379
            while ((t < e) && (*t++ != ':'));
 
1380
            if ((t < e) && (*t == ':')) t++;
 
1381
          case '\t': case ' ':  /* copy field data  */
 
1382
            while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
 
1383
            *v++ = '\n';        /* tie off line */
 
1384
            while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
 
1385
          }
 
1386
                                /* calculate true size */
 
1387
          s.size = v - (char *) s.data;
 
1388
          *v = '\0';            /* tie off results */
 
1389
          stc.text.data = hdr->text.data;
 
1390
          stc.text.size = hdr->text.size;
 
1391
                                /* search header */
 
1392
          if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
 
1393
          else {                /* search failed */
 
1394
            fs_give ((void **) &s.data);
 
1395
            return NIL;
 
1396
          }
 
1397
        }
 
1398
      }
 
1399
      else return NIL;          /* no matching header text */
 
1400
    }
 
1401
                                /* search strings */
 
1402
    if ((pgm->text &&
 
1403
         !mail_search_text (stream,msgno,NIL,pgm->text,LONGT))||
 
1404
        (pgm->body && !mail_search_text (stream,msgno,NIL,pgm->body,NIL)))
 
1405
      return NIL;
 
1406
  }
 
1407
                                /* logical conditions */
 
1408
  for (or = pgm->or; or; or = or->next)
 
1409
    if (!(nntp_search_msg (stream,msgno,or->first,ov) ||
 
1410
          nntp_search_msg (stream,msgno,or->second,ov))) return NIL;
 
1411
  for (not = pgm->not; not; not = not->next)
 
1412
    if (nntp_search_msg (stream,msgno,not->pgm,ov)) return NIL;
 
1413
  return T;
 
1414
}
 
1415
 
 
1416
/* NNTP sort messages
 
1417
 * Accepts: mail stream
 
1418
 *          character set
 
1419
 *          search program
 
1420
 *          sort program
 
1421
 *          option flags
 
1422
 * Returns: vector of sorted message sequences or NIL if error
 
1423
 */
 
1424
 
 
1425
unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
 
1426
                          SORTPGM *pgm,long flags)
 
1427
{
 
1428
  unsigned long i,start,last;
 
1429
  SORTCACHE **sc;
 
1430
  mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
 
1431
  unsigned long *ret = NIL;
 
1432
  sortresults_t sr = (sortresults_t) mail_parameters (NIL,GET_SORTRESULTS,NIL);
 
1433
  if (spg) {                    /* only if a search needs to be done */
 
1434
    int silent = stream->silent;
 
1435
    stream->silent = T;         /* don't pass up mm_searched() events */
 
1436
                                /* search for messages */
 
1437
    mail_search_full (stream,charset,spg,NIL);
 
1438
    stream->silent = silent;    /* restore silence state */
 
1439
  }
 
1440
                                /* initialize progress counters */
 
1441
  pgm->nmsgs = pgm->progress.cached = 0;
 
1442
                                /* pass 1: count messages to sort */
 
1443
  for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
 
1444
    if (mail_elt (stream,i)->searched) {
 
1445
      pgm->nmsgs++;
 
1446
                                /* have this in the sortcache already? */
 
1447
      if (!((SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE))->date) {
 
1448
                                /* no, record as last message */
 
1449
        last = mail_uid (stream,i);
 
1450
                                /* and as first too if needed */
 
1451
        if (!start) start = last;
 
1452
      }
 
1453
    }
 
1454
  if (pgm->nmsgs) {             /* pass 2: load sort cache */
 
1455
    sc = nntp_sort_loadcache (stream,pgm,start,last,flags);
 
1456
                                /* pass 3: sort messages */
 
1457
    if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
 
1458
    fs_give ((void **) &sc);    /* don't need sort vector any more */
 
1459
  }
 
1460
                                /* empty sort results */
 
1461
  else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
 
1462
                                       sizeof (unsigned long));
 
1463
                                /* also return via callback if requested */
 
1464
  if (sr) (*sr) (stream,ret,pgm->nmsgs);
 
1465
  return ret;
 
1466
}
 
1467
 
 
1468
/* Mail load sortcache
 
1469
 * Accepts: mail stream, already searched
 
1470
 *          sort program
 
1471
 *          first UID to OVER
 
1472
 *          last UID to OVER
 
1473
 *          option flags
 
1474
 * Returns: vector of sortcache pointers matching search
 
1475
 */
 
1476
 
 
1477
SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
 
1478
                                 unsigned long start,unsigned long last,
 
1479
                                 long flags)
 
1480
{
 
1481
  unsigned long i;
 
1482
  char c,*s,*t,*v,tmp[MAILTMPLEN];
 
1483
  SORTPGM *pg;
 
1484
  SORTCACHE **sc,*r;
 
1485
  MESSAGECACHE telt;
 
1486
  ADDRESS *adr = NIL;
 
1487
  mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
 
1488
                                /* verify that the sortpgm is OK */
 
1489
  for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
 
1490
  case SORTARRIVAL:             /* sort by arrival date */
 
1491
  case SORTSIZE:                /* sort by message size */
 
1492
  case SORTDATE:                /* sort by date */
 
1493
  case SORTFROM:                /* sort by first from */
 
1494
  case SORTSUBJECT:             /* sort by subject */
 
1495
    break;
 
1496
  case SORTTO:                  /* sort by first to */
 
1497
    mm_notify (stream,"[NNTPSORT] Can't do To-field sorting in NNTP",WARN);
 
1498
    break;
 
1499
  case SORTCC:                  /* sort by first cc */
 
1500
    mm_notify (stream,"[NNTPSORT] Can't do cc-field sorting in NNTP",WARN);
 
1501
    break;
 
1502
  default:
 
1503
    fatal ("Unknown sort function");
 
1504
  }
 
1505
 
 
1506
  if (start) {                  /* messages need to be loaded in sortcache? */
 
1507
                                /* yes, build range */
 
1508
    if (start != last) sprintf (tmp,"%lu-%lu",start,last);
 
1509
    else sprintf (tmp,"%lu",start);
 
1510
                                /* get it from the NNTP server */
 
1511
    if (!nntp_over (stream,tmp)) return mail_sort_loadcache (stream,pgm);
 
1512
    while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
 
1513
                                /* death to embedded newlines */
 
1514
      for (t = v = s; c = *v++;) if ((c != '\012') && (c != '\015')) *t++ = c;
 
1515
      *t++ = '\0';              /* tie off resulting string */
 
1516
                                /* parse OVER response */
 
1517
      if ((i = mail_msgno (stream,atol (s))) &&
 
1518
          (t = strchr (s,'\t')) && (v = strchr (++t,'\t'))) {
 
1519
        *v++ = '\0';            /* tie off subject */
 
1520
                                /* put stripped subject in sortcache */
 
1521
        r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
 
1522
          r->refwd = mail_strip_subject (t,&r->subject);
 
1523
        if (t = strchr (v,'\t')) {
 
1524
          *t++ = '\0';          /* tie off from */
 
1525
          if (adr = rfc822_parse_address (&adr,adr,&v,BADHOST,0)) {
 
1526
            r->from = adr->mailbox;
 
1527
            adr->mailbox = NIL;
 
1528
            mail_free_address (&adr);
 
1529
          }
 
1530
          if (v = strchr (t,'\t')) {
 
1531
            *v++ = '\0';        /* tie off date */
 
1532
            if (mail_parse_date (&telt,t)) r->date = mail_longdate (&telt);
 
1533
            if ((v = strchr (v,'\t')) && (v = strchr (++v,'\t')))
 
1534
              r->size = atol (++v);
 
1535
          }
 
1536
        }
 
1537
      }
 
1538
      fs_give ((void **) &s);
 
1539
    }
 
1540
    if (s) fs_give ((void **) &s);
 
1541
  }
 
1542
 
 
1543
                                /* calculate size of sortcache index */
 
1544
  i = pgm->nmsgs * sizeof (SORTCACHE *);
 
1545
                                /* instantiate the index */
 
1546
  sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
 
1547
                                /* see what needs to be loaded */
 
1548
  for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
 
1549
    if ((mail_elt (stream,i))->searched) {
 
1550
      sc[pgm->progress.cached++] =
 
1551
        r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
 
1552
      r->pgm = pgm;     /* note sort program */
 
1553
      r->num = (flags & SE_UID) ? mail_uid (stream,i) : i;
 
1554
      if (!r->date) r->date = r->num;
 
1555
      if (!r->arrival) r->arrival = mail_uid (stream,i);
 
1556
      if (!r->size) r->size = 1;
 
1557
      if (!r->from) r->from = cpystr ("");
 
1558
      if (!r->to) r->to = cpystr ("");
 
1559
      if (!r->cc) r->cc = cpystr ("");
 
1560
      if (!r->subject) r->subject = cpystr ("");
 
1561
    }
 
1562
  return sc;
 
1563
}
 
1564
 
 
1565
 
 
1566
/* NNTP thread messages
 
1567
 * Accepts: mail stream
 
1568
 *          thread type
 
1569
 *          character set
 
1570
 *          search program
 
1571
 *          option flags
 
1572
 * Returns: thread node tree
 
1573
 */
 
1574
 
 
1575
THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
 
1576
                         SEARCHPGM *spg,long flags)
 
1577
{
 
1578
  return mail_thread_msgs (stream,type,charset,spg,flags,nntp_sort);
 
1579
}
 
1580
 
 
1581
/* NNTP ping mailbox
 
1582
 * Accepts: MAIL stream
 
1583
 * Returns: T if stream alive, else NIL
 
1584
 */
 
1585
 
 
1586
long nntp_ping (MAILSTREAM *stream)
 
1587
{
 
1588
  return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != NNTPSOFTFATAL);
 
1589
}
 
1590
 
 
1591
 
 
1592
/* NNTP check mailbox
 
1593
 * Accepts: MAIL stream
 
1594
 */
 
1595
 
 
1596
void nntp_check (MAILSTREAM *stream)
 
1597
{
 
1598
                                /* never do if no updates */
 
1599
  if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
 
1600
  LOCAL->dirty = NIL;
 
1601
}
 
1602
 
 
1603
 
 
1604
/* NNTP expunge mailbox
 
1605
 * Accepts: MAIL stream
 
1606
 *          sequence to expunge if non-NIL
 
1607
 *          expunge options
 
1608
 * Returns: T if success, NIL if failure
 
1609
 */
 
1610
 
 
1611
long nntp_expunge (MAILSTREAM *stream,char *sequence,long options)
 
1612
{
 
1613
  if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
 
1614
  return LONGT;
 
1615
}
 
1616
 
 
1617
/* NNTP copy message(s)
 
1618
 * Accepts: MAIL stream
 
1619
 *          sequence
 
1620
 *          destination mailbox
 
1621
 *          option flags
 
1622
 * Returns: T if copy successful, else NIL
 
1623
 */
 
1624
 
 
1625
long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
 
1626
{
 
1627
  mailproxycopy_t pc =
 
1628
    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
 
1629
  if (pc) return (*pc) (stream,sequence,mailbox,options);
 
1630
  mm_log ("Copy not valid for NNTP",ERROR);
 
1631
  return NIL;
 
1632
}
 
1633
 
 
1634
 
 
1635
/* NNTP append message from stringstruct
 
1636
 * Accepts: MAIL stream
 
1637
 *          destination mailbox
 
1638
 *          append callback
 
1639
 *          data for callback
 
1640
 * Returns: T if append successful, else NIL
 
1641
 */
 
1642
 
 
1643
long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 
1644
{
 
1645
  mm_log ("Append not valid for NNTP",ERROR);
 
1646
  return NIL;
 
1647
}
 
1648
 
 
1649
/* NNTP open connection
 
1650
 * Accepts: network driver
 
1651
 *          service host list
 
1652
 *          port number
 
1653
 *          service name
 
1654
 *          NNTP open options
 
1655
 * Returns: SEND stream on success, NIL on failure
 
1656
 */
 
1657
 
 
1658
SENDSTREAM *nntp_open_full (NETDRIVER *dv,char **hostlist,char *service,
 
1659
                            unsigned long port,long options)
 
1660
{
 
1661
  SENDSTREAM *stream = NIL;
 
1662
  NETSTREAM *netstream = NIL;
 
1663
  NETMBX mb;
 
1664
  char tmp[MAILTMPLEN];
 
1665
  long extok = LONGT;
 
1666
  NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
 
1667
  sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
 
1668
  if (!(hostlist && *hostlist)) mm_log ("Missing NNTP service host",ERROR);
 
1669
  else do {                     /* try to open connection */
 
1670
    sprintf (tmp,"{%.200s/%.20s}",*hostlist,service ? service : "nntp");
 
1671
    if (!mail_valid_net_parse (tmp,&mb) || mb.anoflag) {
 
1672
      sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
 
1673
      mm_log (tmp,ERROR);
 
1674
    }
 
1675
    else {                      /* light tryssl flag if requested */
 
1676
      mb.trysslflag = (options & NOP_TRYSSL) ? T : NIL;
 
1677
                                /* default port */
 
1678
      if (mb.port) port = mb.port;
 
1679
      else if (!port) port = nntp_port ? nntp_port : NNTPTCPPORT;
 
1680
      if (netstream =           /* try to open ordinary connection */
 
1681
          net_open (&mb,dv,port,
 
1682
                    (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
 
1683
                    "*nntps",nntp_sslport ? nntp_sslport : NNTPSSLPORT)) {
 
1684
        stream = (SENDSTREAM *) fs_get (sizeof (SENDSTREAM));
 
1685
                                /* initialize stream */
 
1686
        memset ((void *) stream,0,sizeof (SENDSTREAM));
 
1687
        stream->netstream = netstream;
 
1688
        stream->host = cpystr ((int) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
 
1689
                               net_host (netstream) : mb.host);
 
1690
        stream->debug = (mb.dbgflag || (options & NOP_DEBUG)) ? T : NIL;
 
1691
        if (mb.loser) stream->loser = T;
 
1692
                                /* process greeting */
 
1693
        switch ((int) nntp_reply (stream)) {
 
1694
        case NNTPGREET:         /* allow posting */
 
1695
          NNTP.post = T;
 
1696
          mm_notify (NIL,stream->reply + 4,(long) NIL);
 
1697
          break;
 
1698
        case NNTPGREETNOPOST:   /* posting not allowed, must be readonly */
 
1699
          NNTP.post = NIL;
 
1700
          break;
 
1701
        default:
 
1702
          mm_log (stream->reply,ERROR);
 
1703
          stream = nntp_close (stream);
 
1704
          break;
 
1705
        }
 
1706
      }
 
1707
    }
 
1708
  } while (!stream && *++hostlist);
 
1709
 
 
1710
                                /* get extensions */
 
1711
  if (stream && extok)
 
1712
    extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
 
1713
                             (mb.authuser[0] ? AU_AUTHUSER : NIL));
 
1714
  if (stream && !dv && stls && NNTP.ext.starttls &&
 
1715
      !mb.sslflag && !mb.notlsflag &&
 
1716
      (nntp_send_work (stream,"STARTTLS",NNTP.ext.multidomain ? mb.host : NIL)
 
1717
       == NNTPTLSSTART)) {
 
1718
    mb.tlsflag = T;             /* TLS OK, get into TLS at this end */
 
1719
    stream->netstream->dtb = ssld;
 
1720
                                /* negotiate TLS */
 
1721
    if (stream->netstream->stream =
 
1722
        (*stls) (stream->netstream->stream,mb.host,
 
1723
                 (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
 
1724
                 (mb.novalidate ? NET_NOVALIDATECERT:NIL)))
 
1725
      extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
 
1726
                               (mb.authuser[0] ? AU_AUTHUSER : NIL));
 
1727
    else {
 
1728
      sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",mb.host);
 
1729
      mm_log (tmp,ERROR);
 
1730
                                /* close without doing QUIT */
 
1731
      if (stream->netstream) net_close (stream->netstream);
 
1732
      stream->netstream = NIL;
 
1733
      stream = nntp_close (stream);
 
1734
    }
 
1735
  }
 
1736
  else if (mb.tlsflag) {        /* user specified /tls but can't do it */
 
1737
    mm_log ("Unable to negotiate TLS with this server",ERROR);
 
1738
    return NIL;
 
1739
  }
 
1740
  if (stream) {                 /* have a session? */
 
1741
    if (mb.user[0]) {           /* yes, have user name? */
 
1742
      if ((int) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
 
1743
                                /* remote name for authentication */
 
1744
        strncpy (mb.host,(int) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
 
1745
                 net_remotehost (netstream) : net_host (netstream),
 
1746
                 NETMAXHOST-1);
 
1747
        mb.host[NETMAXHOST-1] = '\0';
 
1748
      }
 
1749
      if (!nntp_send_auth_work (stream,&mb,tmp,NIL))
 
1750
        stream = nntp_close (stream);
 
1751
    }
 
1752
                                /* authenticate if no-post and not readonly */
 
1753
    else if (!(NNTP.post || (options & NOP_READONLY) ||
 
1754
               nntp_send_auth (stream,NIL))) stream = nntp_close (stream);
 
1755
  }
 
1756
 
 
1757
                                /* in case server demands MODE READER */
 
1758
  if (stream) switch ((int) nntp_send_work (stream,"MODE","READER")) {
 
1759
  case NNTPGREET:
 
1760
    NNTP.post = T;
 
1761
    break;
 
1762
  case NNTPGREETNOPOST:
 
1763
    NNTP.post = NIL;
 
1764
    break;
 
1765
  case NNTPWANTAUTH:            /* server wants auth first, do so and retry */
 
1766
  case NNTPWANTAUTH2:           /* remote name for authentication */
 
1767
    if ((int) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
 
1768
      strncpy (mb.host,(int) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
 
1769
               net_remotehost (netstream) : net_host (netstream),NETMAXHOST-1);
 
1770
      mb.host[NETMAXHOST-1] = '\0';
 
1771
    }
 
1772
    if (nntp_send_auth_work (stream,&mb,tmp,NIL))
 
1773
      switch ((int) nntp_send (stream,"MODE","READER")) {
 
1774
      case NNTPGREET:
 
1775
        NNTP.post = T;
 
1776
        break;
 
1777
      case NNTPGREETNOPOST:
 
1778
        NNTP.post = NIL;
 
1779
        break;
 
1780
      }
 
1781
    else stream = nntp_close (stream);
 
1782
    break;
 
1783
  }
 
1784
  if (stream) {                 /* looks like we have a stream? */
 
1785
                                /* yes, make sure can post if not readonly */
 
1786
    if (!(NNTP.post || (options & NOP_READONLY))) stream = nntp_close (stream);
 
1787
    else if (extok) nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
 
1788
                                     (mb.authuser[0] ? AU_AUTHUSER : NIL));
 
1789
  }
 
1790
  return stream;
 
1791
}
 
1792
 
 
1793
/* NNTP extensions
 
1794
 * Accepts: stream
 
1795
 *          authenticator flags
 
1796
 * Returns: T on success, NIL on failure
 
1797
 */
 
1798
 
 
1799
long nntp_extensions (SENDSTREAM *stream,long flags)
 
1800
{
 
1801
  unsigned long i;
 
1802
  char *t,*args;
 
1803
                                /* zap all old extensions */
 
1804
  memset (&NNTP.ext,0,sizeof (NNTP.ext));
 
1805
  if (stream->loser) return NIL;/* nothing at all for losers */
 
1806
                                /* get server extensions */
 
1807
  switch ((int) nntp_send_work (stream,"LIST","EXTENSIONS")) {
 
1808
  case NNTPEXTOK:               /* what NNTP base spec says */
 
1809
  case NNTPGLIST:               /* some servers do this instead */
 
1810
    break;
 
1811
  default:                      /* no LIST EXTENSIONS on this server */
 
1812
    return NIL;
 
1813
  }
 
1814
  NNTP.ext.ok = T;              /* server offers extensions */
 
1815
  while ((t = net_getline (stream->netstream)) && (t[1] || (*t != '.'))) {
 
1816
    if (stream->debug) mm_dlog (t);
 
1817
                                /* get optional capability arguments */
 
1818
    if (args = strchr (t,' ')) *args++ = '\0';
 
1819
    if (!compare_cstring (t,"LISTGROUP")) NNTP.ext.listgroup = T;
 
1820
    else if (!compare_cstring (t,"OVER")) NNTP.ext.over = T;
 
1821
    else if (!compare_cstring (t,"HDR")) NNTP.ext.hdr = T;
 
1822
    else if (!compare_cstring (t,"PAT")) NNTP.ext.pat = T;
 
1823
    else if (!compare_cstring (t,"STARTTLS")) NNTP.ext.starttls = T;
 
1824
    else if (!compare_cstring (t,"MULTIDOMAIN")) NNTP.ext.multidomain = T;
 
1825
 
 
1826
    else if (!compare_cstring (t,"AUTHINFO") && args) {
 
1827
      char *sasl = NIL;
 
1828
      for (args = strtok (args," "); args; args = strtok (NIL," ")) {
 
1829
        if (!compare_cstring (args,"USER")) NNTP.ext.authuser = T;
 
1830
        else if (((args[0] == 'S') || (args[0] == 's')) &&
 
1831
                 ((args[1] == 'A') || (args[1] == 'a')) &&
 
1832
                 ((args[2] == 'S') || (args[2] == 's')) &&
 
1833
                 ((args[3] == 'L') || (args[3] == 'l')) && (args[4] == ':'))
 
1834
          sasl = args + 5;
 
1835
      }
 
1836
      if (sasl) {               /* if SASL, look up authenticators */
 
1837
        for (sasl = strtok (sasl,","); sasl; sasl = strtok (NIL,","))
 
1838
          if ((i = mail_lookup_auth_name (sasl,flags)) &&
 
1839
              (--i < MAXAUTHENTICATORS))
 
1840
            NNTP.ext.sasl |= (1 << i);
 
1841
                                /* disable LOGIN if PLAIN also advertised */
 
1842
        if ((i = mail_lookup_auth_name ("PLAIN",NIL)) &&
 
1843
            (--i < MAXAUTHENTICATORS) && (NNTP.ext.sasl & (1 << i)) &&
 
1844
            (i = mail_lookup_auth_name ("LOGIN",NIL)) &&
 
1845
            (--i < MAXAUTHENTICATORS)) NNTP.ext.sasl &= ~(1 << i);
 
1846
      }
 
1847
    }
 
1848
    fs_give ((void **) &t);
 
1849
  }
 
1850
  if (t) {                      /* flush end of text indicator */
 
1851
    if (stream->debug) mm_dlog (t);
 
1852
    fs_give ((void **) &t);
 
1853
  }
 
1854
  return LONGT;
 
1855
}
 
1856
 
 
1857
/* NNTP close connection
 
1858
 * Accepts: SEND stream
 
1859
 * Returns: NIL always
 
1860
 */
 
1861
 
 
1862
SENDSTREAM *nntp_close (SENDSTREAM *stream)
 
1863
{
 
1864
  if (stream) {                 /* send "QUIT" */
 
1865
    if (stream->netstream) nntp_send (stream,"QUIT",NIL);
 
1866
                                /* do close actions */
 
1867
    if (stream->netstream) net_close (stream->netstream);
 
1868
    if (stream->host) fs_give ((void **) &stream->host);
 
1869
    if (stream->reply) fs_give ((void **) &stream->reply);
 
1870
    fs_give ((void **) &stream);/* flush the stream */
 
1871
  }
 
1872
  return NIL;
 
1873
}
 
1874
 
 
1875
/* NNTP deliver news
 
1876
 * Accepts: SEND stream
 
1877
 *          message envelope
 
1878
 *          message body
 
1879
 * Returns: T on success, NIL on failure
 
1880
 */
 
1881
 
 
1882
long nntp_mail (SENDSTREAM *stream,ENVELOPE *env,BODY *body)
 
1883
{
 
1884
  long ret;
 
1885
  RFC822BUFFER buf;
 
1886
  char *s,path[MAILTMPLEN],tmp[SENDBUFLEN+1];
 
1887
  long error = NIL;
 
1888
  long retry = NIL;
 
1889
  buf.f = nntp_soutr;           /* initialize buffer */
 
1890
  buf.s = stream->netstream;
 
1891
  buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
 
1892
  tmp[SENDBUFLEN] = '\0';       /* must have additional null guard byte */
 
1893
  /* Gabba gabba hey, we need some brain damage to send netnews!!!
 
1894
   *
 
1895
   * First, we give ourselves a frontal lobotomy, and put in some UUCP
 
1896
   *  syntax.  It doesn't matter that it's completely bogus UUCP, and
 
1897
   *  that UUCP has nothing to do with anything we're doing.  It's been
 
1898
   *  alleged that "Path: not-for-mail" is also acceptable, but we won't
 
1899
   *  make assumptions unless the user says so.
 
1900
   *
 
1901
   * Second, we bop ourselves on the head with a ball-peen hammer.  How
 
1902
   *  dare we be so presumptious as to insert a *comment* in a Date:
 
1903
   *  header line.  Why, we were actually trying to be nice to a human
 
1904
   *  by giving a symbolic timezone (such as PST) in addition to a
 
1905
   *  numeric timezone (such as -0800).  But the gods of news transport
 
1906
   *  will have none of this.  Unix weenies, tried and true, rule!!!
 
1907
   *
 
1908
   * Third, Netscape Collabra server doesn't give the NNTPWANTAUTH error
 
1909
   *  until after requesting and receiving the entire message.  So we can't
 
1910
   *  call rely upon nntp_send() to do the auth retry.
 
1911
   */
 
1912
                                /* RFC-1036 requires this cretinism */
 
1913
  sprintf (path,"Path: %s!%s\015\012",net_localhost (stream->netstream),
 
1914
           env->sender ? env->sender->mailbox :
 
1915
           (env->from ? env->from->mailbox : "not-for-mail"));
 
1916
                                /* here's another cretinism */
 
1917
  if (s = strstr (env->date," (")) *s = NIL;
 
1918
  do if ((ret = nntp_send_work (stream,"POST",NIL)) == NNTPREADY)
 
1919
                                /* output data, return success status */
 
1920
    ret = (net_soutr (stream->netstream,
 
1921
                      nntp_hidepath ? "Path: not-for-mail\015\012" : path) &&
 
1922
           rfc822_output_full (&buf,env,body,T)) ?
 
1923
      nntp_send_work (stream,".",NIL) :
 
1924
      nntp_fake (stream,"NNTP connection broken (message text)");
 
1925
  while (((ret == NNTPWANTAUTH) || (ret == NNTPWANTAUTH2)) &&
 
1926
         nntp_send_auth (stream,LONGT));
 
1927
  if (s) *s = ' ';              /* put the comment in the date back */
 
1928
  if (ret == NNTPOK) return LONGT;
 
1929
  else if (ret < 400) {         /* if not an error reply */
 
1930
    sprintf (tmp,"Unexpected NNTP posting reply code %ld",ret);
 
1931
    mm_log (tmp,WARN);          /* so someone looks at this eventually */
 
1932
    if ((ret >= 200) && (ret < 300)) return LONGT;
 
1933
  }
 
1934
  return NIL;
 
1935
}
 
1936
 
 
1937
/* NNTP send command
 
1938
 * Accepts: SEND stream
 
1939
 *          text
 
1940
 * Returns: reply code
 
1941
 */
 
1942
 
 
1943
long nntp_send (SENDSTREAM *stream,char *command,char *args)
 
1944
{
 
1945
  long ret;
 
1946
  switch ((int) (ret = nntp_send_work (stream,command,args))) {
 
1947
  case NNTPWANTAUTH:            /* authenticate and retry */
 
1948
  case NNTPWANTAUTH2:
 
1949
    if (nntp_send_auth (stream,LONGT))
 
1950
      ret = nntp_send_work (stream,command,args);
 
1951
    else {                      /* we're probably hosed, nuke the session */
 
1952
      nntp_send (stream,"QUIT",NIL);
 
1953
                                /* close net connection */
 
1954
      if (stream->netstream) net_close (stream->netstream);
 
1955
      stream->netstream = NIL;
 
1956
    }
 
1957
  default:                      /* all others just return */
 
1958
    break;
 
1959
  }
 
1960
  return ret;
 
1961
}
 
1962
 
 
1963
 
 
1964
/* NNTP send command worker routine
 
1965
 * Accepts: SEND stream
 
1966
 *          text
 
1967
 * Returns: reply code
 
1968
 */
 
1969
 
 
1970
long nntp_send_work (SENDSTREAM *stream,char *command,char *args)
 
1971
{
 
1972
  long ret;
 
1973
  char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0)
 
1974
                             + 3);
 
1975
  if (!stream->netstream) ret = nntp_fake (stream,"NNTP connection lost");
 
1976
  else {                        /* build the complete command */
 
1977
    if (args) sprintf (s,"%s %s",command,args);
 
1978
    else strcpy (s,command);
 
1979
    if (stream->debug) mail_dlog (s,stream->sensitive);
 
1980
    strcat (s,"\015\012");
 
1981
                                /* send the command */
 
1982
    ret = net_soutr (stream->netstream,s) ? nntp_reply (stream) :
 
1983
      nntp_fake (stream,"NNTP connection broken (command)");
 
1984
  }
 
1985
  fs_give ((void **) &s);
 
1986
  return ret;
 
1987
}
 
1988
 
 
1989
/* NNTP send authentication if needed
 
1990
 * Accepts: SEND stream
 
1991
 *          flags (non-NIL to get new extensions)
 
1992
 * Returns: T if need to redo command, NIL otherwise
 
1993
 */
 
1994
 
 
1995
long nntp_send_auth (SENDSTREAM *stream,long flags)
 
1996
{
 
1997
  NETMBX mb;
 
1998
  char tmp[MAILTMPLEN];
 
1999
                                /* remote name for authentication */
 
2000
  sprintf (tmp,"{%.200s/nntp",(int) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
 
2001
           ((int) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
 
2002
            net_remotehost (stream->netstream) : net_host (stream->netstream)):
 
2003
           stream->host);
 
2004
  if (stream->netstream->dtb ==
 
2005
      (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL))
 
2006
    strcat (tmp,"/ssl");
 
2007
  strcat (tmp,"}<none>");
 
2008
  mail_valid_net_parse (tmp,&mb);
 
2009
  return nntp_send_auth_work (stream,&mb,tmp,flags);
 
2010
}
 
2011
 
 
2012
/* NNTP send authentication worker routine
 
2013
 * Accepts: SEND stream
 
2014
 *          NETMBX structure
 
2015
 *          scratch buffer of length MAILTMPLEN
 
2016
 *          flags (non-NIL to get new extensions)
 
2017
 * Returns: T if authenticated, NIL otherwise
 
2018
 */
 
2019
 
 
2020
long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
 
2021
{
 
2022
  unsigned long trial,auths;
 
2023
  char tmp[MAILTMPLEN],usr[MAILTMPLEN];
 
2024
  AUTHENTICATOR *at;
 
2025
  char *lsterr = NIL;
 
2026
  long ret = NIL;
 
2027
                                /* try SASL first */
 
2028
  for (auths = NNTP.ext.sasl, stream->saslcancel = NIL;
 
2029
       !ret && stream->netstream && auths &&
 
2030
       (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
 
2031
    if (lsterr) {               /* previous authenticator failed? */
 
2032
      sprintf (tmp,"Retrying using %s authentication after %.80s",
 
2033
               at->name,lsterr);
 
2034
      mm_log (tmp,NIL);
 
2035
      fs_give ((void **) &lsterr);
 
2036
    }
 
2037
    trial = 0;                  /* initial trial count */
 
2038
    tmp[0] = '\0';              /* empty buffer */
 
2039
    if (stream->netstream) do {
 
2040
      if (lsterr) {
 
2041
        sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
 
2042
        mm_log (tmp,WARN);
 
2043
        fs_give ((void **) &lsterr);
 
2044
      }
 
2045
      stream->saslcancel = NIL;
 
2046
      if (nntp_send (stream,"AUTHINFO SASL",at->name)) {
 
2047
                                /* hide client authentication responses */
 
2048
        if (!(at->flags & AU_SECURE)) stream->sensitive = T;
 
2049
        if ((*at->client) (nntp_challenge,nntp_response,"nntp",mb,stream,
 
2050
                           &trial,usr)) {
 
2051
          if (stream->replycode == NNTPAUTHED) ret = LONGT;
 
2052
                                /* if main program requested cancellation */
 
2053
          else if (!trial) mm_log ("NNTP Authentication cancelled",ERROR);
 
2054
        }
 
2055
        stream->sensitive = NIL;/* unhide */
 
2056
      }
 
2057
                                /* remember response if error and no cancel */
 
2058
      if (!ret && trial) lsterr = cpystr (stream->reply);
 
2059
    } while (!ret && stream->netstream && trial &&
 
2060
             (trial < nntp_maxlogintrials));
 
2061
  }
 
2062
 
 
2063
  if (lsterr) {                 /* SAIL failed? */
 
2064
    if (!stream->saslcancel) {  /* don't do this if a cancel */
 
2065
      sprintf (tmp,"Can not authenticate to NNTP server: %.80s",lsterr);
 
2066
      mm_log (tmp,ERROR);
 
2067
    }
 
2068
    fs_give ((void **) &lsterr);
 
2069
  }
 
2070
  else if (mb->secflag)         /* no SASL, can't do /secure */
 
2071
    mm_log ("Can't do secure authentication with this server",ERROR);
 
2072
  else if (mb->authuser[0])     /* or /authuser */
 
2073
    mm_log ("Can't do /authuser with this server",ERROR);
 
2074
  /* Always try AUTHINFO USER, even if NNTP.ext.authuser isn't set.  There
 
2075
   * are servers that require it but don't return it as an extension.
 
2076
   */
 
2077
  else for (trial = 0, pwd[0] = 'x';
 
2078
            !ret && pwd[0] && (trial < nntp_maxlogintrials) &&
 
2079
              stream->netstream; ) {
 
2080
    pwd[0] = NIL;               /* get user name and password */
 
2081
    mm_login (mb,usr,pwd,trial++);
 
2082
                                /* do the authentication */
 
2083
    if (pwd[0]) switch ((int) nntp_send_work (stream,"AUTHINFO USER",usr)) {
 
2084
    case NNTPBADCMD:            /* give up if unrecognized command */
 
2085
      mm_log (NNTP.ext.authuser ? stream->reply :
 
2086
              "Can't do AUTHINFO USER to this server",ERROR);
 
2087
      trial = nntp_maxlogintrials;
 
2088
      break;
 
2089
    case NNTPAUTHED:            /* successful authentication */
 
2090
      ret = LONGT;              /* guess no password was needed */
 
2091
      break;
 
2092
    case NNTPWANTPASS:          /* wants password */
 
2093
      stream->sensitive = T;    /* hide this command */
 
2094
      if (nntp_send_work (stream,"AUTHINFO PASS",pwd) == NNTPAUTHED)
 
2095
        ret = LONGT;            /* password OK */
 
2096
      stream->sensitive = NIL;  /* unhide */
 
2097
      if (ret) break;           /* OK if successful */
 
2098
    default:                    /* authentication failed */
 
2099
      mm_log (stream->reply,WARN);
 
2100
      if (trial == nntp_maxlogintrials)
 
2101
        mm_log ("Too many NNTP authentication failures",ERROR);
 
2102
    }
 
2103
                                /* user refused to give a password */
 
2104
    else mm_log ("Login aborted",ERROR);
 
2105
  }
 
2106
  memset (pwd,0,MAILTMPLEN);    /* erase password */
 
2107
                                /* get new extensions if needed */
 
2108
  if (ret && flags) nntp_extensions (stream,(mb->secflag ? AU_SECURE : NIL) |
 
2109
                                     (mb->authuser[0] ? AU_AUTHUSER : NIL));
 
2110
  return ret;
 
2111
}
 
2112
 
 
2113
/* Get challenge to authenticator in binary
 
2114
 * Accepts: stream
 
2115
 *          pointer to returned size
 
2116
 * Returns: challenge or NIL if not challenge
 
2117
 */
 
2118
 
 
2119
void *nntp_challenge (void *s,unsigned long *len)
 
2120
{
 
2121
  char tmp[MAILTMPLEN];
 
2122
  void *ret = NIL;
 
2123
  SENDSTREAM *stream = (SENDSTREAM *) s;
 
2124
  if ((stream->replycode == NNTPCHALLENGE) &&
 
2125
      !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
 
2126
                             strlen (stream->reply + 4),len))) {
 
2127
    sprintf (tmp,"NNTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
 
2128
    mm_log (tmp,ERROR);
 
2129
  }
 
2130
  return ret;
 
2131
}
 
2132
 
 
2133
 
 
2134
/* Send authenticator response in BASE64
 
2135
 * Accepts: MAIL stream
 
2136
 *          string to send
 
2137
 *          length of string
 
2138
 * Returns: T, always
 
2139
 */
 
2140
 
 
2141
long nntp_response (void *s,char *response,unsigned long size)
 
2142
{
 
2143
  SENDSTREAM *stream = (SENDSTREAM *) s;
 
2144
  unsigned long i,j;
 
2145
  char *t,*u;
 
2146
  if (response) {               /* make CRLFless BASE64 string */
 
2147
    if (size) {
 
2148
      for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
 
2149
           j < i; j++) if (t[j] > ' ') *u++ = t[j];
 
2150
      *u = '\0';                /* tie off string */
 
2151
      i = nntp_send_work (stream,t,NIL);
 
2152
      fs_give ((void **) &t);
 
2153
    }
 
2154
    else i = nntp_send_work (stream,"",NIL);
 
2155
  }
 
2156
  else {                        /* abort requested */
 
2157
    i = nntp_send_work (stream,"*",NIL);
 
2158
    stream->saslcancel = T;     /* mark protocol-requested SASL cancel */
 
2159
  }
 
2160
  return LONGT;
 
2161
}
 
2162
 
 
2163
/* NNTP get reply
 
2164
 * Accepts: SEND stream
 
2165
 * Returns: reply code
 
2166
 */
 
2167
 
 
2168
long nntp_reply (SENDSTREAM *stream)
 
2169
{
 
2170
                                /* flush old reply */
 
2171
  if (stream->reply) fs_give ((void **) &stream->reply);
 
2172
                                /* get reply */
 
2173
  if (!(stream->reply = net_getline (stream->netstream)))
 
2174
    return nntp_fake (stream,"NNTP connection broken (response)");
 
2175
  if (stream->debug) mm_dlog (stream->reply);
 
2176
                                /* handle continuation by recursion */
 
2177
  if (stream->reply[3] == '-') return nntp_reply (stream);
 
2178
                                /* return response code */
 
2179
  return stream->replycode = atol (stream->reply);
 
2180
}
 
2181
 
 
2182
 
 
2183
/* NNTP set fake error
 
2184
 * Accepts: SEND stream
 
2185
 *          error text
 
2186
 * Returns: error code
 
2187
 */
 
2188
 
 
2189
long nntp_fake (SENDSTREAM *stream,char *text)
 
2190
{
 
2191
  if (stream->netstream) {      /* close net connection if still open */
 
2192
    net_close (stream->netstream);
 
2193
    stream->netstream = NIL;
 
2194
  }
 
2195
                                /* flush any old reply */
 
2196
  if (stream->reply) fs_give ((void **) &stream->reply);
 
2197
                                /* set up pseudo-reply string */
 
2198
  stream->reply = (char *) fs_get (20+strlen (text));
 
2199
  sprintf (stream->reply,"%ld %s",NNTPSOFTFATAL,text);
 
2200
  return NNTPSOFTFATAL;         /* return error code */
 
2201
}
 
2202
 
 
2203
/* NNTP filter mail
 
2204
 * Accepts: stream
 
2205
 *          string
 
2206
 * Returns: T on success, NIL on failure
 
2207
 */
 
2208
 
 
2209
long nntp_soutr (void *stream,char *s)
 
2210
{
 
2211
  char c,*t;
 
2212
                                /* "." on first line */
 
2213
  if (s[0] == '.') net_soutr (stream,".");
 
2214
                                /* find lines beginning with a "." */
 
2215
  while (t = strstr (s,"\015\012.")) {
 
2216
    c = *(t += 3);              /* remember next character after "." */
 
2217
    *t = '\0';                  /* tie off string */
 
2218
                                /* output prefix */
 
2219
    if (!net_soutr (stream,s)) return NIL;
 
2220
    *t = c;                     /* restore delimiter */
 
2221
    s = t - 1;                  /* push pointer up to the "." */
 
2222
  }
 
2223
                                /* output remainder of text */
 
2224
  return *s ? net_soutr (stream,s) : T;
 
2225
}