"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.2/src/inews.c" (9 Dec 2022, 14031 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "inews.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.6.1_vs_2.6.2.
1 /*
2 * Project : tin - a Usenet reader
3 * Module : inews.c
4 * Author : I. Lea
5 * Created : 1992-03-17
6 * Updated : 2022-10-25
7 * Notes : NNTP built in version of inews
8 *
9 * Copyright (c) 1991-2023 Iain Lea <iain@bricbrac.de>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41 #ifndef TIN_H
42 # include "tin.h"
43 #endif /* !TIN_H */
44 #ifndef TNNTP_H
45 # include "tnntp.h"
46 #endif /* !TNNTP_H */
47
48
49 /*
50 * local prototypes
51 */
52 #ifdef NNTP_INEWS
53 static t_bool submit_inews(char *name, struct t_group *group, char *a_message_id);
54 #endif /* NNTP_INEWS */
55 #if defined(NNTP_INEWS) && !defined(FORGERY)
56 static int sender_needed(char *from, char *sender, const char *charset);
57 #endif /* NNTP_INEWS && !FORGERY */
58
59
60 /*
61 * Submit an article using the NNTP POST command
62 *
63 * TODO: remove mailheaders (To, Cc, Bcc, ...)?
64 */
65 #ifdef NNTP_INEWS
66 static t_bool
67 submit_inews(
68 char *name,
69 struct t_group *group,
70 char *a_message_id)
71 {
72 FILE *fp;
73 char *line;
74 char *ptr, *ptr2;
75 char buf[HEADER_LEN];
76 char from_name[HEADER_LEN];
77 char message_id[HEADER_LEN];
78 char response[NNTP_STRLEN];
79 int auth_error = 0;
80 int respcode;
81 t_bool leave_loop;
82 t_bool id_in_article = FALSE;
83 t_bool ret_code = FALSE;
84 # ifndef FORGERY
85 char sender_hdr[HEADER_LEN];
86 int sender = 0;
87 t_bool ismail = FALSE;
88 # endif /* !FORGERY */
89 # ifdef USE_CANLOCK
90 t_bool can_lock_in_article = FALSE;
91 # endif /* USE_CANLOCK */
92
93 if ((fp = fopen(name, "r")) == NULL) {
94 perror_message(_(txt_cannot_open), name);
95 return ret_code;
96 }
97
98 from_name[0] = '\0';
99 message_id[0] = '\0';
100
101 while ((line = tin_fgets(fp, TRUE)) != NULL) {
102 if (line[0] == '\0') /* end of headers */
103 break;
104
105 if ((ptr = strchr(line, ':'))) {
106 if (ptr - line == 4 && !strncasecmp(line, "From", 4)) {
107 STRCPY(from_name, line);
108 }
109
110 if (ptr - line == 10 && !strncasecmp(line, "Message-ID", 10)) {
111 STRCPY(message_id, ptr + 2);
112 id_in_article = TRUE;
113 }
114 # ifdef USE_CANLOCK
115 if (ptr - line == 11 && !strncasecmp(line, "Cancel-Lock", 11))
116 can_lock_in_article = TRUE;
117 # endif /* USE_CANLOCK */
118 }
119 }
120
121 if ((from_name[0] == '\0') || (from_name[6] == '\0')) {
122 /* we could silently add a From: line here if we want to... */
123 error_message(2, _(txt_error_no_from));
124 fclose(fp);
125 return ret_code;
126 }
127
128 # ifndef FORGERY
129 /*
130 * we should only skip the gnksa_check_from() test if we are going to
131 * post a forged cancel, but inews.c doesn't know anything about the
132 * message type, so we skip the test if FORGERY is set.
133 *
134 * TODO: check at least the local- and domainpart if post_8bit_header
135 * is set
136 *
137 * check for valid From: line
138 */
139 respcode = gnksa_check_from(from_name + 6);
140 if (!(group ? group->attribute->post_8bit_header : tinrc.post_8bit_header) && respcode > GNKSA_OK && respcode < GNKSA_MISSING_REALNAME) { /* error in address */
141 error_message(2, "inews158%s", _(txt_invalid_from), from_name + 6);
142 fclose(fp);
143 return ret_code;
144 }
145 # endif /* !FORGERY */
146
147 do {
148 rewind(fp);
149
150 # ifndef FORGERY
151 if (!disable_sender && (ptr = build_sender())) {
152 # ifdef CHARSET_CONVERSION
153 const char *charset = group ? txt_mime_charsets[group->attribute->mm_network_charset] : txt_mime_charsets[tinrc.mm_network_charset];
154 # else
155 const char *charset = tinrc.mm_charset;
156 # endif /* CHARSET_CONVERSION */
157
158 sender = sender_needed(from_name + 6, ptr, charset);
159 switch (sender) {
160 case -2: /* can't build Sender: */
161 error_message(2, _(txt_invalid_sender), ptr);
162 fclose(fp);
163 return ret_code;
164 /* NOTREACHED */
165 break;
166
167 case -1: /* illegal From: (can't happen as check is done above already) */
168 error_message(2, _(txt_invalid_from), from_name + 6);
169 fclose(fp);
170 return ret_code;
171 /* NOTREACHED */
172 break;
173
174 case 1: /* insert Sender */
175 snprintf(sender_hdr, sizeof(sender_hdr), "Sender: %s", ptr);
176 # ifdef CHARSET_CONVERSION
177 buffer_to_network(sender_hdr, group ? group->attribute->mm_network_charset : tinrc.mm_network_charset);
178 # endif /* CHARSET_CONVERSION */
179 if (!(group ? group->attribute->post_8bit_header : tinrc.post_8bit_header)) {
180 char *p = rfc1522_encode(sender_hdr, charset, ismail);
181
182 STRCPY(sender_hdr, p);
183 free(p);
184 }
185 break;
186
187 case 0: /* no sender needed */
188 default:
189 break;
190 }
191 }
192 # endif /* !FORGERY */
193
194 /*
195 * Send POST command to NNTP server
196 * Receive CONT_POST or ERROR response code from NNTP server
197 */
198 if (nntp_command("POST", CONT_POST, response, sizeof(response)) == NULL) {
199 error_message(2, "%s", response);
200 fclose(fp);
201 return ret_code;
202 }
203
204 /*
205 * check article if it contains a Message-ID header
206 * if not scan response line if it contains a Message-ID
207 * if it's present: use it.
208 */
209 if (message_id[0] == '\0') {
210 /* simple syntax check - locate last '<' */
211 if ((ptr = strrchr(response, '<')) != NULL) {
212 /* search next '>' */
213 if ((ptr2 = strchr(ptr, '>')) != NULL) {
214 /* terminate string */
215 *++ptr2 = '\0';
216 /* check for @ and no whitespaces */
217 if ((strchr(ptr, '@') != NULL) && (strpbrk(ptr, " \t") == NULL))
218 my_strncpy(message_id, ptr, sizeof(message_id) - 1); /* copy Message-ID */
219 }
220 }
221 }
222
223 # ifndef FORGERY
224 /*
225 * Send Path: (and Sender: if needed) headers
226 */
227 snprintf(buf, sizeof(buf), "Path: %s", PATHMASTER);
228 u_put_server(buf);
229 u_put_server("\r\n");
230
231 if (sender == 1) {
232 u_put_server(sender_hdr);
233 u_put_server("\r\n");
234 }
235 # endif /* !FORGERY */
236
237 /*
238 * check if Message-ID comes from the server
239 */
240 if (*message_id) {
241 if (!id_in_article) {
242 snprintf(buf, sizeof(buf), "Message-ID: %s", message_id);
243 u_put_server(buf);
244 u_put_server("\r\n");
245 }
246 # ifdef USE_CANLOCK
247 if (tinrc.cancel_lock_algo && !can_lock_in_article) {
248 char lock[1024];
249 char *lptr;
250
251 lock[0] = '\0';
252 if ((lptr = build_canlock(message_id, get_secret())) != NULL) {
253 STRCPY(lock, lptr);
254 free(lptr);
255 snprintf(buf, sizeof(buf), "Cancel-Lock: %s", lock);
256 u_put_server(buf);
257 u_put_server("\r\n");
258 }
259 }
260 # endif /* USE_CANLOCK */
261 }
262
263 /*
264 * Send article 1 line at a time ending with "."
265 */
266 while ((line = tin_fgets(fp, FALSE)) != NULL) {
267 /*
268 * If line starts with a '.' add another '.' to stop truncation
269 */
270 if (line[0] == '.')
271 u_put_server(".");
272
273 # ifdef USE_CANLOCK
274 /* skip any bogus Cancel-Locks */
275 if (!strlen(line))
276 can_lock_in_article = FALSE; /* don't touch the body */
277
278 if (can_lock_in_article && !id_in_article) {
279 ptr = strchr(line, ':');
280 if (ptr - line != 11 || strncasecmp(line, "Cancel-Lock", 11)) {
281 u_put_server(line);
282 u_put_server("\r\n");
283 }
284 /* TODO: silently add a new Cancel-Lock if message_id is now known? */
285 } else
286 # endif /* USE_CANLOCK */
287 {
288 u_put_server(line);
289 u_put_server("\r\n");
290 }
291 }
292
293 u_put_server(".\r\n");
294 put_server(""); /* flush */
295
296 /*
297 * Receive OK_POSTED or ERROR response code from NNTP server
298 * Don't use get_respcode at this point, because then we would not
299 * recognize if posting has failed due to missing authentication in
300 * which case the complete posting has to be resent.
301 */
302 respcode = get_only_respcode(response, sizeof(response));
303 leave_loop = TRUE;
304
305 /*
306 * Don't leave this loop if we only tried once to post and an
307 * authentication request was received. Leave loop on any other
308 * response or any further authentication requests.
309 *
310 * TODO: add 483 (RFC 3977) support
311 */
312 if (((respcode == ERR_NOAUTH) || (respcode == NEED_AUTHINFO)) && (auth_error++ < 1) && (authenticate(nntp_server, userid, FALSE)))
313 leave_loop = FALSE;
314 } while (!leave_loop);
315
316 fclose(fp);
317
318 /*
319 * FIXME: The displayed message may be wrong if authentication has
320 * failed. (The message will be sth. like "Authentication required"
321 * which is not really wrong but misleading. The problem is that
322 * authenticate() does only return a bool value and not the server
323 * response.)
324 */
325 if (respcode != OK_POSTED) {
326 /* TODO: -> lang.c */
327 error_message(2, "Posting failed (%s)", str_trim(response));
328 return ret_code;
329 }
330
331 /*
332 * scan line if it contains a Message-ID
333 */
334 /* simple syntax check - locate last '<' */
335 if ((ptr = strrchr(response, '<')) != NULL) {
336 /* search next '>' */
337 if ((ptr2 = strchr(response, '>')) != NULL) {
338 /* terminate string */
339 *++ptr2 = '\0';
340 /* check for @ and no whitespaces */
341 if ((strchr(ptr, '@') != NULL) && (strpbrk(ptr, " \t") == NULL))
342 strcpy(a_message_id, ptr); /* copy Message-ID */
343 }
344 }
345
346 # if 0 /* DEBUG */
347 if ((debug & DEBUG_NNTP) && verbose > 1) {
348 if (*message_id && *a_message_id) { /* check if purposed ID matches returned ID */
349 if (strcmp(message_id, a_message_id)) {
350 if (id_in_article)
351 debug_print_file("NNTP", "Returned Message-ID: %s doesn't match given Message-ID: %s", a_message_id, message_id);
352 else
353 debug_print_file("NNTP", "Returned Message-ID: %s doesn't match purposed Message-ID: %s", a_message_id, message_id);
354 }
355 }
356 }
357 # endif /* 0 */
358
359 if (*message_id && (id_in_article || !*a_message_id))
360 strcpy(a_message_id, message_id);
361
362 ret_code = TRUE;
363
364 return ret_code;
365 }
366 #endif /* NNTP_INEWS */
367
368
369 /*
370 * Call submit_inews() if using built in inews, else invoke external inews
371 * prog
372 */
373 t_bool
374 submit_news_file(
375 char *name,
376 struct t_group *group,
377 char *a_message_id)
378 {
379 char buf[PATH_LEN];
380 char *cp = buf;
381 char *fcc;
382 t_bool ret_code;
383 t_bool ismail = FALSE;
384
385 a_message_id[0] = '\0';
386
387 fcc = checknadd_headers(name, group);
388 FreeIfNeeded(fcc); /* we don't use it at the moment */
389
390 rfc15211522_encode(name, txt_mime_encodings[(group ? group->attribute->post_mime_encoding : tinrc.post_mime_encoding)], group, (group ? group->attribute->post_8bit_header : tinrc.post_8bit_header), ismail);
391
392 #ifdef NNTP_INEWS
393 if (read_news_via_nntp && !read_saved_news && !strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
394 ret_code = submit_inews(name, group, a_message_id);
395 else
396 #endif /* NNTP_INEWS */
397 {
398 /* use tinrc.inews_prog or 'inewsdir/inews -h' 'inews -h' */
399 if (strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
400 STRCPY(buf, tinrc.inews_prog);
401 else {
402 if (*inewsdir)
403 joinpath(buf, sizeof(buf), inewsdir, "inews -h");
404 else
405 strcpy(buf, "inews -h");
406 }
407 cp += strlen(cp);
408 sh_format(cp, sizeof(buf) - (size_t) (cp - buf), " < %s", name);
409
410 ret_code = invoke_cmd(buf);
411
412 #ifdef NNTP_INEWS
413 if (!ret_code && read_news_via_nntp && !read_saved_news && strcasecmp(tinrc.inews_prog, INTERNAL_CMD)) {
414 if (prompt_yn(_(txt_post_via_builtin_inews), TRUE)) {
415 ret_code = submit_inews(name, group, a_message_id);
416 if (ret_code) {
417 if (prompt_yn(_(txt_post_via_builtin_inews_only), TRUE) == 1)
418 strcpy(tinrc.inews_prog, INTERNAL_CMD);
419 }
420 }
421 }
422 #endif /* NNTP_INEWS */
423 }
424 return ret_code;
425 }
426
427
428 /*
429 * returnvalues: 1 = Sender needed
430 * 0 = no Sender needed
431 * -1 = error (no '.' and/or '@' in From) [unused]
432 * -2 = error (no '.' and/or '@' in Sender)
433 */
434 #if defined(NNTP_INEWS) && !defined(FORGERY)
435 static int
436 sender_needed(
437 char *from,
438 char *sender,
439 const char *charset)
440 {
441 char *from_at_pos;
442 char *sender_at_pos;
443 char *sender_dot_pos;
444 char *p;
445 char from_addr[HEADER_LEN];
446 char sender_addr[HEADER_LEN];
447 char sender_line[HEADER_LEN];
448 char sender_name[HEADER_LEN];
449 int r;
450
451 # ifdef DEBUG
452 if (debug & DEBUG_MISC) {
453 wait_message(3, "sender_needed From:=[%s]", from);
454 wait_message(3, "sender_needed Sender:=[%s]", sender);
455 }
456 # endif /* DEBUG */
457
458 /* extract address */
459 strip_name(from, from_addr);
460
461 snprintf(sender_line, sizeof(sender_line), "Sender: %s", sender);
462
463 p = rfc1522_encode(sender_line, charset, FALSE);
464 r = gnksa_do_check_from(p + 8, sender_addr, sender_name);
465 if (r > GNKSA_OK && r < GNKSA_MISSING_REALNAME) {
466 free(p);
467 return -2;
468 }
469 free(p);
470
471 from_at_pos = strchr(from_addr, '@');
472 if ((sender_at_pos = strchr(sender_addr, '@')))
473 sender_dot_pos = strchr(sender_at_pos, '.');
474 else /* this case is caught by the gnksa_do_check_from() code above; anyway ... */
475 return -2;
476
477 if (from_at_pos == NULL || sender_dot_pos == NULL) /* as we build From and check Sender above this shouldn't happen at all */
478 return -2;
479
480 if (strncasecmp(from_addr, sender_addr, (size_t) (from_at_pos - from_addr)))
481 return 1; /* login differs */
482
483 if (strcasecmp(from_at_pos, sender_at_pos) && (strcasecmp(from_at_pos + 1, sender_dot_pos + 1)))
484 return 1; /* domainname differs */
485
486 return 0;
487 }
488 #endif /* NNTP_INEWS && !FORGERY */