"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.1/src/inews.c" (22 Dec 2021, 13996 Bytes) of package /linux/misc/tin-2.6.1.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.0_vs_2.6.1.
1 /*
2 * Project : tin - a Usenet reader
3 * Module : inews.c
4 * Author : I. Lea
5 * Created : 1992-03-17
6 * Updated : 2021-02-23
7 * Notes : NNTP built in version of inews
8 *
9 * Copyright (c) 1991-2022 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 #if 0
60 # ifdef HAVE_NETDB_H
61 # include <netdb.h>
62 # endif /* HAVE_NETDB_H */
63
64 # ifdef HAVE_SYS_SOCKET_H
65 # include <sys/socket.h>
66 # endif /* HAVE_SYS_SOCKET_H */
67 # ifdef HAVE_NETINET_IN_H
68 # include <netinet/in.h>
69 # endif /* HAVE_NETINET_IN_H */
70 #endif /* 0 */
71
72
73 /*
74 * Submit an article using the NNTP POST command
75 *
76 * TODO: remove mailheaders (To, Cc, Bcc, ...)?
77 */
78 #ifdef NNTP_INEWS
79 static t_bool
80 submit_inews(
81 char *name,
82 struct t_group *group,
83 char *a_message_id)
84 {
85 FILE *fp;
86 char *line;
87 char *ptr, *ptr2;
88 char buf[HEADER_LEN];
89 char from_name[HEADER_LEN];
90 char message_id[HEADER_LEN];
91 char response[NNTP_STRLEN];
92 int auth_error = 0;
93 int respcode;
94 t_bool leave_loop;
95 t_bool id_in_article = FALSE;
96 t_bool ret_code = FALSE;
97 # ifndef FORGERY
98 char sender_hdr[HEADER_LEN];
99 int sender = 0;
100 t_bool ismail = FALSE;
101 # endif /* !FORGERY */
102 # ifdef USE_CANLOCK
103 t_bool can_lock_in_article = FALSE;
104 # endif /* USE_CANLOCK */
105
106 if ((fp = fopen(name, "r")) == NULL) {
107 perror_message(_(txt_cannot_open), name);
108 return ret_code;
109 }
110
111 from_name[0] = '\0';
112 message_id[0] = '\0';
113
114 while ((line = tin_fgets(fp, TRUE)) != NULL) {
115 if (line[0] == '\0') /* end of headers */
116 break;
117
118 if ((ptr = strchr(line, ':'))) {
119 if (ptr - line == 4 && !strncasecmp(line, "From", 4)) {
120 STRCPY(from_name, line);
121 }
122
123 if (ptr - line == 10 && !strncasecmp(line, "Message-ID", 10)) {
124 STRCPY(message_id, ptr + 2);
125 id_in_article = TRUE;
126 }
127 # ifdef USE_CANLOCK
128 if (ptr - line == 11 && !strncasecmp(line, "Cancel-Lock", 11))
129 can_lock_in_article = TRUE;
130 # endif /* USE_CANLOCK */
131 }
132 }
133
134 if ((from_name[0] == '\0') || (from_name[6] == '\0')) {
135 /* we could silently add a From: line here if we want to... */
136 error_message(2, _(txt_error_no_from));
137 fclose(fp);
138 return ret_code;
139 }
140
141 # ifndef FORGERY
142 /*
143 * we should only skip the gnksa_check_from() test if we are going to
144 * post a forged cancel, but inews.c doesn't know anything about the
145 * message type, so we skip the test if FORGERY is set.
146 *
147 * TODO: check at least the local- and domainpart if post_8bit_header
148 * is set
149 *
150 * check for valid From: line
151 */
152 respcode = gnksa_check_from(from_name + 6);
153 if (!(group ? group->attribute->post_8bit_header : tinrc.post_8bit_header) && respcode > GNKSA_OK && respcode < GNKSA_MISSING_REALNAME) { /* error in address */
154 error_message(2, "inews158%s", _(txt_invalid_from), from_name + 6);
155 fclose(fp);
156 return ret_code;
157 }
158 # endif /* !FORGERY */
159
160 do {
161 rewind(fp);
162
163 # ifndef FORGERY
164 if (!disable_sender && (ptr = build_sender())) {
165 # ifdef CHARSET_CONVERSION
166 const char *charset = group ? txt_mime_charsets[group->attribute->mm_network_charset] : txt_mime_charsets[tinrc.mm_network_charset];
167 # else
168 const char *charset = tinrc.mm_charset;
169 # endif /* CHARSET_CONVERSION */
170
171 sender = sender_needed(from_name + 6, ptr, charset);
172 switch (sender) {
173 case -2: /* can't build Sender: */
174 error_message(2, _(txt_invalid_sender), ptr);
175 fclose(fp);
176 return ret_code;
177 /* NOTREACHED */
178 break;
179
180 case -1: /* illegal From: (can't happen as check is done above already) */
181 error_message(2, _(txt_invalid_from), from_name + 6);
182 fclose(fp);
183 return ret_code;
184 /* NOTREACHED */
185 break;
186
187 case 1: /* insert Sender */
188 snprintf(sender_hdr, sizeof(sender_hdr), "Sender: %s", ptr);
189 # ifdef CHARSET_CONVERSION
190 buffer_to_network(sender_hdr, group ? group->attribute->mm_network_charset : tinrc.mm_network_charset);
191 # endif /* CHARSET_CONVERSION */
192 if (!(group ? group->attribute->post_8bit_header : tinrc.post_8bit_header)) {
193 char *p = rfc1522_encode(sender_hdr, charset, ismail);
194
195 STRCPY(sender_hdr, p);
196 free(p);
197 }
198 break;
199
200 case 0: /* no sender needed */
201 default:
202 break;
203 }
204 }
205 # endif /* !FORGERY */
206
207 /*
208 * Send POST command to NNTP server
209 * Receive CONT_POST or ERROR response code from NNTP server
210 */
211 if (nntp_command("POST", CONT_POST, response, sizeof(response)) == NULL) {
212 error_message(2, "%s", response);
213 fclose(fp);
214 return ret_code;
215 }
216
217 /*
218 * check article if it contains a Message-ID header
219 * if not scan response line if it contains a Message-ID
220 * if it's present: use it.
221 */
222 if (message_id[0] == '\0') {
223 /* simple syntax check - locate last '<' */
224 if ((ptr = strrchr(response, '<')) != NULL) {
225 /* search next '>' */
226 if ((ptr2 = strchr(ptr, '>')) != NULL) {
227 /* terminate string */
228 *++ptr2 = '\0';
229 /* check for @ and no whitespaces */
230 if ((strchr(ptr, '@') != NULL) && (strpbrk(ptr, " \t") == NULL))
231 my_strncpy(message_id, ptr, sizeof(message_id) - 1); /* copy Message-ID */
232 }
233 }
234 }
235
236 # ifndef FORGERY
237 /*
238 * Send Path: (and Sender: if needed) headers
239 */
240 snprintf(buf, sizeof(buf), "Path: %s", PATHMASTER);
241 u_put_server(buf);
242 u_put_server("\r\n");
243
244 if (sender == 1) {
245 u_put_server(sender_hdr);
246 u_put_server("\r\n");
247 }
248 # endif /* !FORGERY */
249
250 /*
251 * check if Message-ID comes from the server
252 */
253 if (*message_id) {
254 if (!id_in_article) {
255 snprintf(buf, sizeof(buf), "Message-ID: %s", message_id);
256 u_put_server(buf);
257 u_put_server("\r\n");
258 }
259 # ifdef USE_CANLOCK
260 if (tinrc.cancel_lock_algo && !can_lock_in_article) {
261 char lock[1024];
262 char *lptr;
263
264 lock[0] = '\0';
265 if ((lptr = build_canlock(message_id, get_secret())) != NULL) {
266 STRCPY(lock, lptr);
267 free(lptr);
268 snprintf(buf, sizeof(buf), "Cancel-Lock: %s", lock);
269 u_put_server(buf);
270 u_put_server("\r\n");
271 }
272 }
273 # endif /* USE_CANLOCK */
274 }
275
276 /*
277 * Send article 1 line at a time ending with "."
278 */
279 while ((line = tin_fgets(fp, FALSE)) != NULL) {
280 /*
281 * If line starts with a '.' add another '.' to stop truncation
282 */
283 if (line[0] == '.')
284 u_put_server(".");
285
286 # ifdef USE_CANLOCK
287 /* skip any bogus Cancel-Locks */
288 if (!strlen(line))
289 can_lock_in_article = FALSE; /* don't touch the body */
290
291 if (can_lock_in_article && !id_in_article) {
292 ptr = strchr(line, ':');
293 if (ptr - line != 11 || strncasecmp(line, "Cancel-Lock", 11)) {
294 u_put_server(line);
295 u_put_server("\r\n");
296 }
297 /* TODO: silently add a new Cancel-Lock if message_id is now known? */
298 } else
299 # endif /* USE_CANLOCK */
300 {
301 u_put_server(line);
302 u_put_server("\r\n");
303 }
304 }
305
306 u_put_server(".\r\n");
307 put_server(""); /* flush */
308
309 /*
310 * Receive OK_POSTED or ERROR response code from NNTP server
311 * Don't use get_respcode at this point, because then we would not
312 * recognize if posting has failed due to missing authentication in
313 * which case the complete posting has to be resent.
314 */
315 respcode = get_only_respcode(response, sizeof(response));
316 leave_loop = TRUE;
317
318 /*
319 * Don't leave this loop if we only tried once to post and an
320 * authentication request was received. Leave loop on any other
321 * response or any further authentication requests.
322 *
323 * TODO: add 483 (RFC 3977) support
324 */
325 if (((respcode == ERR_NOAUTH) || (respcode == NEED_AUTHINFO)) && (auth_error++ < 1) && (authenticate(nntp_server, userid, FALSE)))
326 leave_loop = FALSE;
327 } while (!leave_loop);
328
329 fclose(fp);
330
331 /*
332 * FIXME: The displayed message may be wrong if authentication has
333 * failed. (The message will be sth. like "Authentication required"
334 * which is not really wrong but misleading. The problem is that
335 * authenticate() does only return a bool value and not the server
336 * response.)
337 */
338 if (respcode != OK_POSTED) {
339 /* TODO: -> lang.c */
340 error_message(2, "Posting failed (%s)", str_trim(response));
341 return ret_code;
342 }
343
344 /*
345 * scan line if it contains a Message-ID
346 */
347 /* simple syntax check - locate last '<' */
348 if ((ptr = strrchr(response, '<')) != NULL) {
349 /* search next '>' */
350 if ((ptr2 = strchr(response, '>')) != NULL) {
351 /* terminate string */
352 *++ptr2 = '\0';
353 /* check for @ and no whitespaces */
354 if ((strchr(ptr, '@') != NULL) && (strpbrk(ptr, " \t") == NULL))
355 strcpy(a_message_id, ptr); /* copy Message-ID */
356 }
357 }
358
359 # if 0
360 if (*message_id && *a_message_id) { /* check if returned ID matches purposed ID */
361 if (strcmp(message_id, a_message_id)) {
362 ; /* shouldn't happen - warn user? */
363 }
364 }
365 # endif /* 0 */
366
367 if (*message_id && (id_in_article || !*a_message_id))
368 strcpy(a_message_id, message_id);
369
370 ret_code = TRUE;
371
372 return ret_code;
373 }
374 #endif /* NNTP_INEWS */
375
376
377 /*
378 * Call submit_inews() if using built in inews, else invoke external inews
379 * prog
380 */
381 t_bool
382 submit_news_file(
383 char *name,
384 struct t_group *group,
385 char *a_message_id)
386 {
387 char buf[PATH_LEN];
388 char *cp = buf;
389 char *fcc;
390 t_bool ret_code;
391 t_bool ismail = FALSE;
392
393 a_message_id[0] = '\0';
394
395 fcc = checknadd_headers(name, group);
396 FreeIfNeeded(fcc); /* we don't use it at the moment */
397
398 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);
399
400 #ifdef NNTP_INEWS
401 if (read_news_via_nntp && !read_saved_news && !strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
402 ret_code = submit_inews(name, group, a_message_id);
403 else
404 #endif /* NNTP_INEWS */
405 {
406 /* use tinrc.inews_prog or 'inewsdir/inews -h' 'inews -h' */
407 if (strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
408 STRCPY(buf, tinrc.inews_prog);
409 else {
410 if (*inewsdir)
411 joinpath(buf, sizeof(buf), inewsdir, "inews -h");
412 else
413 strcpy(buf, "inews -h");
414 }
415 cp += strlen(cp);
416 sh_format(cp, sizeof(buf) - (size_t) (cp - buf), " < %s", name);
417
418 ret_code = invoke_cmd(buf);
419
420 #ifdef NNTP_INEWS
421 if (!ret_code && read_news_via_nntp && !read_saved_news && strcasecmp(tinrc.inews_prog, INTERNAL_CMD)) {
422 if (prompt_yn(_(txt_post_via_builtin_inews), TRUE)) {
423 ret_code = submit_inews(name, group, a_message_id);
424 if (ret_code) {
425 if (prompt_yn(_(txt_post_via_builtin_inews_only), TRUE) == 1)
426 strcpy(tinrc.inews_prog, INTERNAL_CMD);
427 }
428 }
429 }
430 #endif /* NNTP_INEWS */
431 }
432 return ret_code;
433 }
434
435
436 /*
437 * returnvalues: 1 = Sender needed
438 * 0 = no Sender needed
439 * -1 = error (no '.' and/or '@' in From) [unused]
440 * -2 = error (no '.' and/or '@' in Sender)
441 */
442 #if defined(NNTP_INEWS) && !defined(FORGERY)
443 static int
444 sender_needed(
445 char *from,
446 char *sender,
447 const char *charset)
448 {
449 char *from_at_pos;
450 char *sender_at_pos;
451 char *sender_dot_pos;
452 char *p;
453 char from_addr[HEADER_LEN];
454 char sender_addr[HEADER_LEN];
455 char sender_line[HEADER_LEN];
456 char sender_name[HEADER_LEN];
457 int r;
458
459 # ifdef DEBUG
460 if (debug & DEBUG_MISC) {
461 wait_message(3, "sender_needed From:=[%s]", from);
462 wait_message(3, "sender_needed Sender:=[%s]", sender);
463 }
464 # endif /* DEBUG */
465
466 /* extract address */
467 strip_name(from, from_addr);
468
469 snprintf(sender_line, sizeof(sender_line), "Sender: %s", sender);
470
471 p = rfc1522_encode(sender_line, charset, FALSE);
472 r = gnksa_do_check_from(p + 8, sender_addr, sender_name);
473 if (r > GNKSA_OK && r < GNKSA_MISSING_REALNAME) {
474 free(p);
475 return -2;
476 }
477 free(p);
478
479 from_at_pos = strchr(from_addr, '@');
480 if ((sender_at_pos = strchr(sender_addr, '@')))
481 sender_dot_pos = strchr(sender_at_pos, '.');
482 else /* this case is caught by the gnksa_do_check_from() code above; anyway ... */
483 return -2;
484
485 if (from_at_pos == NULL || sender_dot_pos == NULL) /* as we build From and check Sender above this shouldn't happen at all */
486 return -2;
487
488 if (strncasecmp(from_addr, sender_addr, (size_t) (from_at_pos - from_addr)))
489 return 1; /* login differs */
490
491 if (strcasecmp(from_at_pos, sender_at_pos) && (strcasecmp(from_at_pos + 1, sender_dot_pos + 1)))
492 return 1; /* domainname differs */
493
494 return 0;
495 }
496 #endif /* NNTP_INEWS && !FORGERY */