Mercurial > noffle
view 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 source
/* post.c $Id: post.c 100 2000-05-18 12:17:23Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #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" #include "portable.h" struct OverInfo { Str subject; Str from; Str date; Str msgId; Str ref; size_t bytes; size_t lines; }; struct Article { 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, NULL, NULL, FALSE, NULL, { "", "", "", "", "", 0, 0 } }; /* Add the article to a group. */ static Bool addToGroup( const char * grp ) { Over * over; const char *msgId; over = new_Over( article.over.subject, article.over.from, article.over.date, article.over.msgId, article.over.ref, article.over.bytes, article.over.lines ); msgId = article.over.msgId; Cont_read( grp ); Cont_app( over ); Log_dbg( "Added message '%s' to group '%s'.", msgId, grp ); if ( !article.posted ) { Log_inf( "Added '%s' to database.", msgId ); if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) || ! Db_storeArt ( msgId, DynStr_str( article.text ) ) ) return FALSE; article.posted = TRUE; } else { Str t; const char *xref; xref = Db_xref( msgId ); Log_dbg( "Adding '%s' to Xref of '%s'", grp, msgId ); snprintf( t, MAXCHAR, "%s %s:%i", xref, grp, Ov_numb( over ) ); Db_setXref( msgId, t ); } Cont_write(); 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 ) { 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; }