"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 */