Mercurial > noffle
diff src/post.c @ 88:1fcdced0246e noffle
[svn] Move posting code to post.c, add command line posting
author | bears |
---|---|
date | Thu, 18 May 2000 13:17:23 +0100 |
parents | 7250be163ec4 |
children | eb522db0d032 |
line wrap: on
line diff
--- a/src/post.c Thu May 18 13:11:05 2000 +0100 +++ b/src/post.c Thu May 18 13:17:23 2000 +0100 @@ -1,7 +1,7 @@ /* post.c - $Id: post.c 70 2000-05-12 17:35:00Z enz $ + $Id: post.c 100 2000-05-18 12:17:23Z bears $ */ #if HAVE_CONFIG_H @@ -12,10 +12,14 @@ #include "post.h" #include <string.h> #include "common.h" +#include "configfile.h" #include "content.h" +#include "control.h" #include "database.h" #include "group.h" +#include "itemlist.h" #include "log.h" +#include "outgoing.h" #include "over.h" #include "protocol.h" #include "util.h" @@ -34,80 +38,24 @@ struct Article { - const char * text; - Bool posted; + DynStr *text; /* Processed article text */ + ItemList *newsgroups; /* Newsgroups for dispatch */ + ItemList *control; /* Control message? NULL if not */ + Bool posted; /* Has it been put in the article database? */ + const char *server; /* Server for external post */ struct OverInfo over; }; -static struct Article article = { NULL, FALSE, { "", "", "", "", "", 0, 0 } }; - -static void -getOverInfo( struct OverInfo * o ) -{ - const char *p = article.text; - Str line, field, value; - - o->bytes = strlen( p ); - - while( p != NULL ) - { - p = Utl_getHeaderLn( line, p ); - if ( line[ 0 ] == '\0' ) - break; - - /* Look for headers we need to stash. */ - if ( Prt_getField( field, value, line ) ) - { - if ( strcmp( field, "subject" ) == 0 ) - Utl_cpyStr( o->subject, value ); - else if ( strcmp ( field, "from" ) == 0 ) - Utl_cpyStr( o->from, value ); - else if ( strcmp ( field, "date" ) == 0 ) - Utl_cpyStr( o->date, value ); - else if ( strcmp ( field, "references" ) == 0 ) - Utl_cpyStr( o->ref, value ); - else if ( strcmp ( field, "message-id" ) == 0 ) - Utl_cpyStr( o->msgId, value ); - } - } - - /* Move to start of body and count lines. */ - for ( p++, o->lines = 0; *p != '\0'; p++ ) - if ( *p == '\n' ) - o->lines++; -} - -/* Register an article for posting. */ -Bool -Post_open( const char * text ) -{ - if ( article.text != NULL ) - { - Log_err( "Busy article in Post_open." ); - return FALSE; - } - - memset( &article.over, 0, sizeof( article.over ) ); - article.text = text; - getOverInfo( &article.over ); - - if ( Db_contains( article.over.msgId ) ) - { - Log_err( "Duplicate article %s.", article.over.msgId ); - return FALSE; - } - - return TRUE; -} - +static struct Article article = { NULL, NULL, NULL, FALSE, NULL, + { "", "", "", "", "", 0, 0 } }; /* Add the article to a group. */ -Bool -Post_add ( const char * grp ) +static Bool +addToGroup( const char * grp ) { Over * over; const char *msgId; - + over = new_Over( article.over.subject, article.over.from, article.over.date, @@ -126,7 +74,7 @@ { Log_inf( "Added '%s' to database.", msgId ); if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) - || ! Db_storeArt ( msgId, article.text ) ) + || ! Db_storeArt ( msgId, DynStr_str( article.text ) ) ) return FALSE; article.posted = TRUE; } @@ -145,12 +93,347 @@ Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); return TRUE; } + +static Bool +checkPostableNewsgroup( Bool localOnly ) +{ + const char *grp; + Bool knownGrp = FALSE; + Bool postAllowedGrp = FALSE; + Bool local; + + /* + Check at least one group is known. Look for external group and + set server. + */ + article.server = NULL; + for( grp = Itl_first( article.newsgroups ); + grp != NULL; + grp = Itl_next( article.newsgroups ) ) + { + if ( Grp_exists( grp ) ) + { + local = Grp_local( grp ); + if ( localOnly && ! local ) + continue; + knownGrp = TRUE; + switch( Grp_postAllow( grp ) ) + { + case 'n': + if ( localOnly ) + postAllowedGrp = TRUE; + break; + case 'y': + postAllowedGrp = TRUE; + break; + default: + if ( localOnly ) + postAllowedGrp = TRUE; + else + /* Can't post to moderated local groups. */ + postAllowedGrp = ! local; + break; + } + if ( postAllowedGrp && ! local && article.server == NULL ) + article.server = Grp_server( grp ); + if ( postAllowedGrp && article.server != NULL ) + break; + } + } + + if ( ! knownGrp ) + { + Log_err( "No known group in Newsgroups header field" ); + return FALSE; + } + else if ( ! postAllowedGrp ) + { + Log_err( "No group permits posting" ); + return FALSE; + } + + return TRUE; +} + +/* Get article text, check for validity & build overview. */ +static Bool +getArticleText( const char *p ) +{ + DynStr * s; + Str line, field, value; + Bool replyToFound; + + s = new_DynStr( 10000 ); + article.text = s; + + memset( &article.over, 0, sizeof( article.over ) ); + replyToFound = FALSE; + + /* Grab header lines first, getting overview info as we go. */ + while ( ( p = Utl_getHeaderLn( line, p ) ) != NULL + && line[ 0 ] != '\0' + && Prt_getField( field, value, line ) ) + { + /* Look for headers we need to stash. */ + if ( strcmp( field, "subject" ) == 0 ) + { + Utl_cpyStr( article.over.subject, value ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "from" ) == 0 ) + { + Utl_cpyStr( article.over.from, value ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "date" ) == 0 ) + { + Utl_cpyStr( article.over.date, value ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "references" ) == 0 ) + { + Utl_cpyStr( article.over.ref, value ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "message-id" ) == 0 ) + Utl_cpyStr( article.over.msgId, value ); + else if ( strcmp ( field, "newsgroups" ) == 0 ) + { + article.newsgroups = new_Itl( value, " ,\n\t" ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "control" ) == 0 ) + { + article.control = new_Itl( value, " " ); + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "reply-to" ) == 0 ) + { + replyToFound = TRUE; + DynStr_appLn( s, line ); + } + else if ( strcmp ( field, "x-sender" ) == 0 ) + { + DynStr_app( s, "X-NOFFLE-X-Sender: " ); + DynStr_appLn( s, value ); + } + else if ( strcmp ( field, "xref" ) == 0 ) + Log_inf( "Xref header in post ignored" ); + else + DynStr_appLn( s, line ); + } + + /* Now sort header-related issues */ + if ( article.over.from[ 0 ] == '\0' ) + { + Log_err( "Posted message has no From field" ); + return FALSE; + } + if ( article.over.subject[ 0 ] == '\0' ) + { + Log_err( "Posted message has no Subject field" ); + return FALSE; + } + if ( article.newsgroups == NULL || Itl_count( article.newsgroups) == 0 ) + { + Log_err( "Posted message has no valid Newsgroups field" ); + return FALSE; + } + if ( Cfg_replaceMsgId() ) + { + Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); + Log_dbg( "Replacing Message-ID with '%s'", article.over.msgId ); + } + else if ( article.over.msgId[ 0 ] == '\0' ) + { + Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); + Log_inf( "Adding missing Message-ID '%s'", article.over.msgId ); + } + else if ( ! Prt_isValidMsgId( article.over.msgId ) ) + { + Log_ntc( "Replacing invalid Message-ID '%s'", article.over.msgId ); + Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); + } + DynStr_app( s, "Message-ID: " ); + DynStr_appLn( s, article.over.msgId ); + if ( ! replyToFound ) + { + Log_dbg( "Adding Reply-To field to posted message." ); + DynStr_app( s, "Reply-To: " ); + DynStr_appLn( s, article.over.from ); + } + if ( article.over.date[ 0 ] == '\0' ) + { + time_t t; + + time( &t ); + Utl_rfc822Date( t, article.over.date ); + DynStr_app( s, "Date: " ); + DynStr_appLn( s, article.over.date ); + } + if ( p == NULL || p[ 0 ] == '\0' ) + { + Log_err( "Posted message has no body" ); + return FALSE; + } + + /* Add the empty line separating header and body */ + DynStr_appLn( s, "" ); + + /* Now pop on the rest of the body and count the lines & bytes */ + DynStr_app( s, p ); + for ( p++, article.over.lines = 0; *p != '\0'; p++ ) + if ( *p == '\n' ) + article.over.lines++; + article.over.bytes = DynStr_len( s ); + + return TRUE; +} + +/* Add article to outgoing if needs be */ +static Bool +postExternal( void ) +{ + if ( article.server == NULL ) + return TRUE; + + if ( ! Out_add( article.server, article.over.msgId, article.text ) ) + { + Log_err( "Cannot add posted article to outgoing directory" ); + return FALSE; + } + + return TRUE; +} + +/* Cancel and return TRUE if need to send cancel message on to server. */ +static Bool +controlCancel( const char *cancelId ) +{ + return ( Ctrl_cancel( cancelId ) == CANCEL_NEEDS_MSG ); +} + +/* + It's a control message. Currently we only know about 'cancel' + messages; others are passed on for outside groups, and logged + as ignored for local groups. + */ +static Bool +handleControl( void ) +{ + const char *grp; + const char *op; + + op = Itl_first( article.control ); + if ( op == NULL ) + { + Log_err( "Malformed control line." ); + return TRUE; + } + else if ( strcasecmp( op, "cancel" ) == 0 ) + { + if ( ! controlCancel( Itl_next( article.control ) ) ) + return TRUE; /* Handled entirely locally */ + } + else + { + /* Log 'can't do' for internal groups. */ + for( grp = Itl_first( article.newsgroups ); + grp != NULL; + grp = Itl_next( article.newsgroups ) ) + { + if ( Grp_exists( grp ) && Grp_local( grp ) ) + Log_inf( "Ignoring control '%s' for '%s'.", op, grp ); + } + } + + return postExternal(); +} + +static Bool +postArticle( Bool localOnly ) +{ + const char *grp; + Bool err; + Bool postLocal; + Bool local; + + err = FALSE; + postLocal = Cfg_postLocal(); + + /* Run round & post locally */ + for( grp = Itl_first( article.newsgroups ); + grp != NULL; + grp = Itl_next( article.newsgroups ) ) + { + local = Grp_local( grp ); + if ( localOnly && ! local ) + continue; + if ( ( local || postLocal ) + && ( Grp_postAllow( grp ) == 'y' || localOnly ) ) + err = addToGroup( grp ) && err; + } + + if ( localOnly ) + return err; + else + return postExternal() && err; +} + +/* Register an article for posting. */ +Bool +Post_open( const char * text ) +{ + if ( article.text != NULL ) + { + Log_err( "Busy article in Post_open." ); + return FALSE; + } + + if ( ! getArticleText( text ) ) + return FALSE; + + if ( Db_contains( article.over.msgId ) ) + { + Log_err( "Duplicate article %s.", article.over.msgId ); + return FALSE; + } + + return TRUE; +} + +/* Process the posting */ +Bool +Post_post( Bool localOnly ) +{ + if ( ! checkPostableNewsgroup( localOnly ) ) + return FALSE; + + return ( article.control == NULL ) + ? ! postArticle( localOnly ) + : ! handleControl(); +} /* Done with article - tidy up. */ void Post_close( void ) { - article.text = NULL; + if ( article.text != NULL ) + { + del_DynStr( article.text ); + article.text = NULL; + } + if ( article.newsgroups != NULL ) + { + del_Itl( article.newsgroups ); + article.newsgroups = NULL; + } + if ( article.control != NULL ) + { + del_Itl( article.control ); + article.control = NULL; + } article.posted = FALSE; + article.server = NULL; }