"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.4/src/inews.c" (20 Nov 2019, 13976 Bytes) of package /linux/misc/tin-2.4.4.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.3_vs_2.4.4.

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