"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.5/src/inews.c" (1 Dec 2020, 14093 Bytes) of package /linux/misc/tin-2.4.5.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.4.4_vs_2.4.5.

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