1 /* artio.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "list.h"
9 #include "hash.h"
10 #include "ngdata.h"
11 #include "nntpclient.h"
12 #include "datasrc.h"
13 #include "nntp.h"
14 #include "cache.h"
15 #include "rthread.h"
16 #include "head.h"
17 #include "mime.h"
18 #include "term.h"
19 #include "ng.h"
20 #include "art.h"
21 #include "search.h"
22 #include "artstate.h"
23 #include "bits.h"
24 #include "final.h"
25 #include "util.h"
26 #include "util2.h"
27 #include "color.h"
28 #include "decode.h"
29 #include "INTERN.h"
30 #include "artio.h"
31
32 void
artio_init()33 artio_init()
34 {
35 artbuf_size = 8 * 1024;
36 artbuf = safemalloc(artbuf_size);
37 clear_artbuf();
38 }
39
40 /* open an article, unless it's already open */
41
42 FILE*
artopen(artnum,pos)43 artopen(artnum, pos)
44 ART_NUM artnum;
45 ART_POS pos;
46 {
47 char artname[MAXFILENAME]; /* filename of current article */
48 ARTICLE* ap = article_find(artnum);
49
50 if (!ap || !artnum || (ap->flags & (AF_EXISTS|AF_FAKE)) != AF_EXISTS) {
51 errno = ENOENT;
52 return NULL;
53 }
54 if (openart == artnum) { /* this article is already open? */
55 seekart(pos); /* yes: just seek the file */
56 return artfp; /* and say we succeeded */
57 }
58 artclose();
59 retry_open:
60 #ifdef SUPPORT_NNTP
61 if (datasrc->flags & DF_REMOTE)
62 nntp_body(artnum);
63 else
64 #endif
65 {
66 sprintf(artname,"%ld",(long)artnum);
67 artfp = fopen(artname,"r");
68 /*artio_setbuf(artfp);$$*/
69 }
70 if (!artfp) {
71 #ifdef ETIMEDOUT
72 if (errno == ETIMEDOUT)
73 goto retry_open;
74 #endif
75 if (errno == EINTR)
76 goto retry_open;
77 uncache_article(ap,FALSE);
78 } else {
79 #ifdef LINKART
80 #ifdef SUPPORT_NNTP
81 if (!(datasrc->flags & DF_REMOTE))
82 #endif
83 {
84 char tmpbuf[256];
85 char* s;
86
87 if (!fstat(fileno(artfp),&filestat)
88 && filestat.st_size < sizeof tmpbuf) {
89 fgets(tmpbuf,sizeof tmpbuf,artfp);
90 if (FILE_REF(tmpbuf)) { /* is a "link" to another article */
91 fclose(artfp);
92 if ((s = index(tmpbuf,'\n')) != NULL)
93 *s = '\0';
94 if (!(artfp = fopen(tmpbuf,"r")))
95 uncache_article(ap,FALSE);
96 else {
97 if (*linkartname)
98 free(linkartname);
99 linkartname = savestr(tmpbuf);
100 }
101 }
102 }
103 }
104 #endif
105 openart = artnum; /* remember what we did here */
106 seekart(pos);
107 }
108 return artfp; /* and return either fp or NULL */
109 }
110
111 void
artclose()112 artclose()
113 {
114 if (artfp != NULL) { /* article still open? */
115 #ifdef SUPPORT_NNTP
116 if (datasrc->flags & DF_REMOTE)
117 nntp_finishbody(FB_DISCARD);
118 #endif
119 fclose(artfp); /* close it */
120 artfp = NULL; /* and tell the world */
121 openart = 0;
122 clear_artbuf();
123 }
124 }
125
126 int
seekart(pos)127 seekart(pos)
128 ART_POS pos;
129 {
130 #ifdef SUPPORT_NNTP
131 if (datasrc->flags & DF_REMOTE)
132 return nntp_seekart(pos);
133 #endif
134 return fseek(artfp,(long)pos,0);
135 }
136
137 ART_POS
tellart()138 tellart()
139 {
140 #ifdef SUPPORT_NNTP
141 if (datasrc->flags & DF_REMOTE)
142 return nntp_tellart();
143 #endif
144 return (ART_POS)ftell(artfp);
145 }
146
147 char*
readart(s,limit)148 readart(s, limit)
149 char* s;
150 int limit;
151 {
152 #ifdef SUPPORT_NNTP
153 if (datasrc->flags & DF_REMOTE)
154 return nntp_readart(s,limit);
155 #endif
156 return fgets(s,limit,artfp);
157 }
158
159 void
clear_artbuf()160 clear_artbuf()
161 {
162 *artbuf = '\0';
163 artbuf_pos = artbuf_seek = artbuf_len = 0;
164 }
165
166 int
seekartbuf(pos)167 seekartbuf(pos)
168 ART_POS pos;
169 {
170 if (!do_hiding)
171 return seekart(pos);
172
173 pos -= htype[PAST_HEADER].minpos;
174 artbuf_pos = artbuf_len;
175
176 while (artbuf_pos < pos) {
177 if (!readartbuf(FALSE))
178 return -1;
179 }
180
181 artbuf_pos = pos;
182
183 return 0;
184 }
185
186 char*
readartbuf(view_inline)187 readartbuf(view_inline)
188 bool_int view_inline;
189 {
190 char* bp;
191 char* s;
192 int read_offset, line_offset, filter_offset, extra_offset, len, o;
193 int word_wrap, extra_chars = 0;
194 int read_something = 0;
195
196 if (!do_hiding) {
197 bp = readart(art_line,(sizeof art_line)-1);
198 artbuf_pos = artbuf_seek = tellart() - htype[PAST_HEADER].minpos;
199 return bp;
200 }
201 if (artbuf_pos == artsize - htype[PAST_HEADER].minpos)
202 return NULL;
203 bp = artbuf + artbuf_pos;
204 if (*bp == '\001' || *bp == '\002') {
205 bp++;
206 artbuf_pos++;
207 }
208 if (*bp) {
209 for (s = bp; *s && !AT_NL(*s); s++) ;
210 if (*s) {
211 len = s - bp + 1;
212 goto done;
213 }
214 read_offset = line_offset = filter_offset = s - bp;
215 }
216 else
217 read_offset = line_offset = filter_offset = 0;
218
219 read_more:
220 extra_offset = mime_state == HTMLTEXT_MIME? 1024 : 0;
221 o = read_offset + extra_offset;
222 if (artbuf_size < artbuf_pos + o + LBUFLEN) {
223 artbuf_size += LBUFLEN * 4;
224 artbuf = saferealloc(artbuf,artbuf_size);
225 bp = artbuf + artbuf_pos;
226 }
227 switch (mime_state) {
228 case IMAGE_MIME:
229 case AUDIO_MIME:
230 break;
231 default:
232 read_something = 1;
233 /* The -1 leaves room for appending a newline, if needed */
234 if (!readart(bp+o, artbuf_size-artbuf_pos-o-1)) {
235 if (!read_offset) {
236 *bp = '\0';
237 len = 0;
238 bp = NULL;
239 goto done;
240 }
241 strcpy(bp+o, "\n");
242 read_something = -1;
243 }
244 len = strlen(bp+o) + read_offset;
245 if (bp[len+extra_offset-1] != '\n') {
246 if (read_something >= 0) {
247 read_offset = len;
248 goto read_more;
249 }
250 strcpy(bp + len++ + extra_offset, "\n");
251 }
252 if (!is_mime)
253 goto done;
254 o = line_offset + extra_offset;
255 mime_SetState(bp+o);
256 if (bp[o] == '\0') {
257 strcpy(bp+o, "\n");
258 len = line_offset+1;
259 }
260 break;
261 }
262 mime_switch:
263 switch (mime_state) {
264 case ISOTEXT_MIME:
265 #if 0 /*def CHARSUBST*/
266 charsubst = "a"; /*$$*/
267 #endif
268 mime_state = TEXT_MIME;
269 /* FALL THROUGH */
270 case TEXT_MIME:
271 case HTMLTEXT_MIME:
272 if (mime_section->encoding == MENCODE_QPRINT) {
273 o = line_offset + extra_offset;
274 len = qp_decodestring(bp+o, bp+o, 0) + line_offset;
275 if (len == line_offset || bp[len+extra_offset-1] != '\n') {
276 if (read_something >= 0) {
277 read_offset = line_offset = len;
278 goto read_more;
279 }
280 strcpy(bp + len++ + extra_offset, "\n");
281 }
282 }
283 else if (mime_section->encoding == MENCODE_BASE64) {
284 o = line_offset + extra_offset;
285 len = b64_decodestring(bp+o, bp+o) + line_offset;
286 if ((s = index(bp+o, '\n')) == NULL) {
287 if (read_something >= 0) {
288 read_offset = line_offset = len;
289 goto read_more;
290 }
291 strcpy(bp + len++ + extra_offset, "\n");
292 }
293 else {
294 extra_chars += len;
295 len = s - bp - extra_offset + 1;
296 extra_chars -= len;
297 }
298 }
299 if (mime_state != HTMLTEXT_MIME)
300 break;
301 o = filter_offset + extra_offset;
302 len = filter_html(bp+filter_offset, bp+o) + filter_offset;
303 if (len == filter_offset || (s = index(bp,'\n')) == NULL) {
304 if (read_something >= 0) {
305 read_offset = line_offset = filter_offset = len;
306 goto read_more;
307 }
308 strcpy(bp + len++, "\n");
309 extra_chars = 0;
310 }
311 else {
312 extra_chars = len;
313 len = s - bp + 1;
314 extra_chars -= len;
315 }
316 break;
317 case DECODE_MIME: {
318 MIMECAP_ENTRY* mcp;
319 mcp = mime_FindMimecapEntry(mime_section->type_name,
320 MCF_NEEDSTERMINAL|MCF_COPIOUSOUTPUT);
321 if (mcp) {
322 int save_term_line = term_line;
323 nowait_fork = TRUE;
324 color_object(COLOR_MIMEDESC, 1);
325 if (decode_piece(mcp,bp) != 0) {
326 strcpy(bp = artbuf + artbuf_pos, art_line);
327 mime_SetState(bp);
328 if (mime_state == DECODE_MIME)
329 mime_state = SKIP_MIME;
330 }
331 else
332 mime_state = SKIP_MIME;
333 color_pop();
334 chdir_newsdir();
335 erase_line(FALSE);
336 nowait_fork = FALSE;
337 first_view = artline;
338 term_line = save_term_line;
339 if (mime_state != SKIP_MIME)
340 goto mime_switch;
341 }
342 /* FALL THROUGH */
343 }
344 case SKIP_MIME: {
345 MIME_SECT* mp = mime_section;
346 while ((mp = mp->prev) != NULL && !mp->boundary_len) ;
347 if (!mp) {
348 artbuf_len = artbuf_pos;
349 artsize = artbuf_len + htype[PAST_HEADER].minpos;
350 read_something = 0;
351 bp = NULL;
352 }
353 else if (read_something >= 0) {
354 *bp = '\0';
355 read_offset = line_offset = filter_offset = 0;
356 goto read_more;
357 }
358 else
359 *bp = '\0';
360 len = 0;
361 break;
362 }
363 case END_OF_MIME:
364 if (mime_section->prev)
365 mime_state = SKIP_MIME;
366 else {
367 #ifdef SUPPORT_NNTP
368 if (datasrc->flags & DF_REMOTE) {
369 nntp_finishbody(FB_SILENT);
370 raw_artsize = nntp_artsize();
371 }
372 #endif
373 seekart(raw_artsize);
374 }
375 /* FALL THROUGH */
376 case BETWEEN_MIME:
377 len = strlen(multipart_separator) + 1;
378 if (extra_offset && filter_offset) {
379 extra_chars = len + 1;
380 len = o = read_offset + 1;
381 bp[o-1] = '\n';
382 }
383 else {
384 o = -1;
385 artbuf_pos++;
386 bp++;
387 }
388 sprintf(bp+o,"\002%s\n",multipart_separator);
389 break;
390 case UNHANDLED_MIME:
391 mime_state = SKIP_MIME;
392 *bp++ = '\001';
393 artbuf_pos++;
394 mime_Description(mime_section,bp,tc_COLS);
395 len = strlen(bp);
396 break;
397 case ALTERNATE_MIME:
398 mime_state = SKIP_MIME;
399 *bp++ = '\001';
400 artbuf_pos++;
401 sprintf(bp,"[Alternative: %s]\n", mime_section->type_name);
402 len = strlen(bp);
403 break;
404 case IMAGE_MIME:
405 case AUDIO_MIME:
406 if (!mime_article.total && !multimedia_mime)
407 multimedia_mime = TRUE;
408 /* FALL THROUGH */
409 default:
410 if (view_inline && first_view < artline
411 && (mime_section->flags & MSF_INLINE))
412 mime_state = DECODE_MIME;
413 else
414 mime_state = SKIP_MIME;
415 *bp++ = '\001';
416 artbuf_pos++;
417 mime_Description(mime_section,bp,tc_COLS);
418 len = strlen(bp);
419 break;
420 }
421
422 done:
423 word_wrap = tc_COLS - word_wrap_offset;
424 if (read_something && word_wrap_offset >= 0 && word_wrap > 20 && bp) {
425 char* cp;
426 for (cp = bp; *cp && (s = index(cp, '\n')) != NULL; cp = s+1) {
427 if (s - cp > tc_COLS) {
428 char* t;
429 do {
430 for (t = cp+word_wrap; *t!=' ' && *t!='\t' && t > cp; t--) ;
431 if (t == cp) {
432 for (t = cp+word_wrap; *t!=' ' && *t!='\t' && t<=cp+tc_COLS; t++) ;
433 if (t > cp+tc_COLS) {
434 t = cp + tc_COLS - 1;
435 continue;
436 }
437 }
438 if (cp == bp) {
439 extra_chars += len;
440 len = t - bp + 1;
441 extra_chars -= len;
442 }
443 *t = wrapped_nl;
444 if (t[1] == ' ' || t[1] == '\t') {
445 int spaces = 1;
446 for (t++; *++t == ' ' || *t == '\t'; spaces++) ;
447 safecpy(t-spaces,t,extra_chars);
448 extra_chars -= spaces;
449 t -= spaces + 1;
450 }
451 } while (s - (cp = t+1) > word_wrap);
452 }
453 }
454 }
455 artbuf_pos += len;
456 if (read_something) {
457 artbuf_seek = tellart();
458 artbuf_len = artbuf_pos + extra_chars;
459 if (artsize >= 0)
460 artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos;
461 }
462
463 return bp;
464 }
465