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