"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/inews.c" (12 Oct 2016, 13931 Bytes) of package /linux/misc/tin-2.4.1.tar.gz:


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.0_vs_2.4.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : inews.c
    4  *  Author    : I. Lea
    5  *  Created   : 1992-03-17
    6  *  Updated   : 2013-11-27
    7  *  Notes     : NNTP built in version of inews
    8  *
    9  * Copyright (c) 1991-2017 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  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. The name of the author may not be used to endorse or promote
   21  *    products derived from this software without specific prior written
   22  *    permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
   25  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
   28  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   30  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   35  */
   36 
   37 
   38 #ifndef TIN_H
   39 #   include "tin.h"
   40 #endif /* !TIN_H */
   41 #ifndef TNNTP_H
   42 #   include "tnntp.h"
   43 #endif /* !TNNTP_H */
   44 
   45 #if defined(NNTP_INEWS) && !defined(FORGERY)
   46 #   define PATHMASTER   "not-for-mail"
   47 #endif /* NNTP_INEWS && !FORGERY */
   48 
   49 
   50 /*
   51  * local prototypes
   52  */
   53 #ifdef NNTP_INEWS
   54     static t_bool submit_inews(char *name, struct t_group *group, char *a_message_id);
   55 #endif /* NNTP_INEWS */
   56 #if defined(NNTP_INEWS) && !defined(FORGERY)
   57     static int sender_needed(char *from, char *sender, const char *charset);
   58 #endif /* NNTP_INEWS && !FORGERY */
   59 
   60 #if 0
   61 #   ifdef HAVE_NETDB_H
   62 #       include <netdb.h>
   63 #   endif /* HAVE_NETDB_H */
   64 
   65 #   ifdef HAVE_SYS_SOCKET_H
   66 #       include <sys/socket.h>
   67 #   endif /* HAVE_SYS_SOCKET_H */
   68 #   ifdef HAVE_NETINET_IN_H
   69 #       include <netinet/in.h>
   70 #   endif /* HAVE_NETINET_IN_H */
   71 #endif /* 0 */
   72 
   73 
   74 /*
   75  * Submit an article using the NNTP POST command
   76  *
   77  * TODO: remove mailheaders (To, Cc, Bcc, ...)?
   78  */
   79 #ifdef NNTP_INEWS
   80 static t_bool
   81 submit_inews(
   82     char *name,
   83     struct t_group *group,
   84     char *a_message_id)
   85 {
   86     FILE *fp;
   87     char *line;
   88     char *ptr, *ptr2;
   89     char buf[HEADER_LEN];
   90     char from_name[HEADER_LEN];
   91     char message_id[HEADER_LEN];
   92     char response[NNTP_STRLEN];
   93     int auth_error = 0;
   94     int respcode;
   95     t_bool leave_loop;
   96     t_bool id_in_article = FALSE;
   97     t_bool ret_code = FALSE;
   98 #   ifndef FORGERY
   99     char sender_hdr[HEADER_LEN];
  100     int sender = 0;
  101     t_bool ismail = FALSE;
  102 #   endif /* !FORGERY */
  103 #   ifdef USE_CANLOCK
  104     t_bool can_lock_in_article = FALSE;
  105 #   endif /* USE_CANLOCK */
  106 
  107     if ((fp = fopen(name, "r")) == NULL) {
  108         perror_message(_(txt_cannot_open), name);
  109         return ret_code;
  110     }
  111 
  112     from_name[0] = '\0';
  113     message_id[0] = '\0';
  114 
  115     while ((line = tin_fgets(fp, TRUE)) != NULL) {
  116         if (line[0] == '\0') /* end of headers */
  117             break;
  118 
  119         if ((ptr = strchr(line, ':'))) {
  120             if (ptr - line == 4 && !strncasecmp(line, "From", 4)) {
  121                 STRCPY(from_name, line);
  122             }
  123 
  124             if (ptr - line == 10 && !strncasecmp(line, "Message-ID", 10)) {
  125                 STRCPY(message_id, ptr + 2);
  126                 id_in_article = TRUE;
  127             }
  128 #   ifdef USE_CANLOCK
  129             if (ptr - line == 11 && !strncasecmp(line, "Cancel-Lock", 11))
  130                 can_lock_in_article = TRUE;
  131 #   endif /* USE_CANLOCK */
  132         }
  133     }
  134 
  135     if ((from_name[0] == '\0') || (from_name[6] == '\0')) {
  136         /* we could silently add a From: line here if we want to... */
  137         error_message(2, _(txt_error_no_from));
  138         fclose(fp);
  139         return ret_code;
  140     }
  141 
  142 #   ifndef FORGERY
  143     /*
  144      * we should only skip the gnksa_check_from() test if we are going to
  145      * post a forged cancel, but inews.c doesn't know anything about the
  146      * message type, so we skip the test if FORGERY is set.
  147      *
  148      * TODO: check at least the local- and domainpart if post_8bit_header
  149      *       is set
  150      *
  151      * check for valid From: line
  152      */
  153     if (!(group ? group->attribute->post_8bit_header : tinrc.post_8bit_header) && GNKSA_OK != gnksa_check_from(from_name + 6)) { /* error in address */
  154         error_message(2, _(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 (!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 && 0 == strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
  402         ret_code = submit_inews(name, group, a_message_id);
  403     else
  404 #endif /* NNTP_INEWS */
  405         {
  406 #ifdef M_UNIX
  407             /* use tinrc.inews_prog or 'inewsdir/inews -h' 'inews -h' */
  408             if (0 != strcasecmp(tinrc.inews_prog, INTERNAL_CMD))
  409                 STRCPY(buf, tinrc.inews_prog);
  410             else {
  411                 if (*inewsdir)
  412                     joinpath(buf, sizeof(buf), inewsdir, "inews -h");
  413                 else
  414                     strcpy(buf, "inews -h");
  415             }
  416             cp += strlen(cp);
  417             sh_format(cp, sizeof(buf) - (cp - buf), " < %s", name);
  418 #else
  419             make_post_cmd(cp, name);
  420 #endif /* M_UNIX */
  421 
  422             ret_code = invoke_cmd(buf);
  423 
  424 #ifdef NNTP_INEWS
  425             if (!ret_code && read_news_via_nntp && !read_saved_news && 0 != 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 
  462 #   ifdef DEBUG
  463     if (debug & DEBUG_MISC) {
  464         wait_message(3, "sender_needed From:=[%s]", from);
  465         wait_message(3, "sender_needed Sender:=[%s]", sender);
  466     }
  467 #   endif /* DEBUG */
  468 
  469     /* extract address */
  470     strip_name(from, from_addr);
  471 
  472     snprintf(sender_line, sizeof(sender_line), "Sender: %s", sender);
  473 
  474     p = rfc1522_encode(sender_line, charset, FALSE);
  475     if (GNKSA_OK != gnksa_do_check_from(p + 8, sender_addr, sender_name)) {
  476         free(p);
  477         return -2;
  478     }
  479     free(p);
  480 
  481     from_at_pos = strchr(from_addr, '@');
  482     if ((sender_at_pos = strchr(sender_addr, '@')))
  483         sender_dot_pos = strchr(sender_at_pos, '.');
  484     else /* this case is catched by the gnksa_do_check_from() code above; anyway ... */
  485         return -2;
  486 
  487     if (from_at_pos == NULL || sender_dot_pos == NULL) /* as we build From and check Sender above this shouldn't happen at all */
  488         return -2;
  489 
  490     if (strncasecmp(from_addr, sender_addr, (from_at_pos - from_addr)))
  491         return 1; /* login differs */
  492 
  493     if (strcasecmp(from_at_pos, sender_at_pos) && (strcasecmp(from_at_pos + 1, sender_dot_pos + 1)))
  494         return 1; /* domainname differs */
  495 
  496     return 0;
  497 }
  498 #endif /* NNTP_INEWS && !FORGERY */