Mercurial > noffle
comparison 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 |
comparison
equal
deleted
inserted
replaced
87:bf8c97460fd7 | 88:1fcdced0246e |
---|---|
1 /* | 1 /* |
2 post.c | 2 post.c |
3 | 3 |
4 $Id: post.c 70 2000-05-12 17:35:00Z enz $ | 4 $Id: post.c 100 2000-05-18 12:17:23Z bears $ |
5 */ | 5 */ |
6 | 6 |
7 #if HAVE_CONFIG_H | 7 #if HAVE_CONFIG_H |
8 #include <config.h> | 8 #include <config.h> |
9 #endif | 9 #endif |
10 | 10 |
11 #include <stdio.h> | 11 #include <stdio.h> |
12 #include "post.h" | 12 #include "post.h" |
13 #include <string.h> | 13 #include <string.h> |
14 #include "common.h" | 14 #include "common.h" |
15 #include "configfile.h" | |
15 #include "content.h" | 16 #include "content.h" |
17 #include "control.h" | |
16 #include "database.h" | 18 #include "database.h" |
17 #include "group.h" | 19 #include "group.h" |
20 #include "itemlist.h" | |
18 #include "log.h" | 21 #include "log.h" |
22 #include "outgoing.h" | |
19 #include "over.h" | 23 #include "over.h" |
20 #include "protocol.h" | 24 #include "protocol.h" |
21 #include "util.h" | 25 #include "util.h" |
22 #include "portable.h" | 26 #include "portable.h" |
23 | 27 |
32 size_t lines; | 36 size_t lines; |
33 }; | 37 }; |
34 | 38 |
35 struct Article | 39 struct Article |
36 { | 40 { |
37 const char * text; | 41 DynStr *text; /* Processed article text */ |
38 Bool posted; | 42 ItemList *newsgroups; /* Newsgroups for dispatch */ |
43 ItemList *control; /* Control message? NULL if not */ | |
44 Bool posted; /* Has it been put in the article database? */ | |
45 const char *server; /* Server for external post */ | |
39 struct OverInfo over; | 46 struct OverInfo over; |
40 }; | 47 }; |
41 | 48 |
42 static struct Article article = { NULL, FALSE, { "", "", "", "", "", 0, 0 } }; | 49 static struct Article article = { NULL, NULL, NULL, FALSE, NULL, |
43 | 50 { "", "", "", "", "", 0, 0 } }; |
44 static void | |
45 getOverInfo( struct OverInfo * o ) | |
46 { | |
47 const char *p = article.text; | |
48 Str line, field, value; | |
49 | |
50 o->bytes = strlen( p ); | |
51 | |
52 while( p != NULL ) | |
53 { | |
54 p = Utl_getHeaderLn( line, p ); | |
55 if ( line[ 0 ] == '\0' ) | |
56 break; | |
57 | |
58 /* Look for headers we need to stash. */ | |
59 if ( Prt_getField( field, value, line ) ) | |
60 { | |
61 if ( strcmp( field, "subject" ) == 0 ) | |
62 Utl_cpyStr( o->subject, value ); | |
63 else if ( strcmp ( field, "from" ) == 0 ) | |
64 Utl_cpyStr( o->from, value ); | |
65 else if ( strcmp ( field, "date" ) == 0 ) | |
66 Utl_cpyStr( o->date, value ); | |
67 else if ( strcmp ( field, "references" ) == 0 ) | |
68 Utl_cpyStr( o->ref, value ); | |
69 else if ( strcmp ( field, "message-id" ) == 0 ) | |
70 Utl_cpyStr( o->msgId, value ); | |
71 } | |
72 } | |
73 | |
74 /* Move to start of body and count lines. */ | |
75 for ( p++, o->lines = 0; *p != '\0'; p++ ) | |
76 if ( *p == '\n' ) | |
77 o->lines++; | |
78 } | |
79 | |
80 /* Register an article for posting. */ | |
81 Bool | |
82 Post_open( const char * text ) | |
83 { | |
84 if ( article.text != NULL ) | |
85 { | |
86 Log_err( "Busy article in Post_open." ); | |
87 return FALSE; | |
88 } | |
89 | |
90 memset( &article.over, 0, sizeof( article.over ) ); | |
91 article.text = text; | |
92 getOverInfo( &article.over ); | |
93 | |
94 if ( Db_contains( article.over.msgId ) ) | |
95 { | |
96 Log_err( "Duplicate article %s.", article.over.msgId ); | |
97 return FALSE; | |
98 } | |
99 | |
100 return TRUE; | |
101 } | |
102 | |
103 | 51 |
104 /* Add the article to a group. */ | 52 /* Add the article to a group. */ |
105 Bool | 53 static Bool |
106 Post_add ( const char * grp ) | 54 addToGroup( const char * grp ) |
107 { | 55 { |
108 Over * over; | 56 Over * over; |
109 const char *msgId; | 57 const char *msgId; |
110 | 58 |
111 over = new_Over( article.over.subject, | 59 over = new_Over( article.over.subject, |
112 article.over.from, | 60 article.over.from, |
113 article.over.date, | 61 article.over.date, |
114 article.over.msgId, | 62 article.over.msgId, |
115 article.over.ref, | 63 article.over.ref, |
124 | 72 |
125 if ( !article.posted ) | 73 if ( !article.posted ) |
126 { | 74 { |
127 Log_inf( "Added '%s' to database.", msgId ); | 75 Log_inf( "Added '%s' to database.", msgId ); |
128 if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) | 76 if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) |
129 || ! Db_storeArt ( msgId, article.text ) ) | 77 || ! Db_storeArt ( msgId, DynStr_str( article.text ) ) ) |
130 return FALSE; | 78 return FALSE; |
131 article.posted = TRUE; | 79 article.posted = TRUE; |
132 } | 80 } |
133 else | 81 else |
134 { | 82 { |
143 | 91 |
144 Cont_write(); | 92 Cont_write(); |
145 Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); | 93 Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); |
146 return TRUE; | 94 return TRUE; |
147 } | 95 } |
96 | |
97 static Bool | |
98 checkPostableNewsgroup( Bool localOnly ) | |
99 { | |
100 const char *grp; | |
101 Bool knownGrp = FALSE; | |
102 Bool postAllowedGrp = FALSE; | |
103 Bool local; | |
104 | |
105 /* | |
106 Check at least one group is known. Look for external group and | |
107 set server. | |
108 */ | |
109 article.server = NULL; | |
110 for( grp = Itl_first( article.newsgroups ); | |
111 grp != NULL; | |
112 grp = Itl_next( article.newsgroups ) ) | |
113 { | |
114 if ( Grp_exists( grp ) ) | |
115 { | |
116 local = Grp_local( grp ); | |
117 if ( localOnly && ! local ) | |
118 continue; | |
119 knownGrp = TRUE; | |
120 switch( Grp_postAllow( grp ) ) | |
121 { | |
122 case 'n': | |
123 if ( localOnly ) | |
124 postAllowedGrp = TRUE; | |
125 break; | |
126 case 'y': | |
127 postAllowedGrp = TRUE; | |
128 break; | |
129 default: | |
130 if ( localOnly ) | |
131 postAllowedGrp = TRUE; | |
132 else | |
133 /* Can't post to moderated local groups. */ | |
134 postAllowedGrp = ! local; | |
135 break; | |
136 } | |
137 if ( postAllowedGrp && ! local && article.server == NULL ) | |
138 article.server = Grp_server( grp ); | |
139 if ( postAllowedGrp && article.server != NULL ) | |
140 break; | |
141 } | |
142 } | |
143 | |
144 if ( ! knownGrp ) | |
145 { | |
146 Log_err( "No known group in Newsgroups header field" ); | |
147 return FALSE; | |
148 } | |
149 else if ( ! postAllowedGrp ) | |
150 { | |
151 Log_err( "No group permits posting" ); | |
152 return FALSE; | |
153 } | |
154 | |
155 return TRUE; | |
156 } | |
157 | |
158 /* Get article text, check for validity & build overview. */ | |
159 static Bool | |
160 getArticleText( const char *p ) | |
161 { | |
162 DynStr * s; | |
163 Str line, field, value; | |
164 Bool replyToFound; | |
165 | |
166 s = new_DynStr( 10000 ); | |
167 article.text = s; | |
168 | |
169 memset( &article.over, 0, sizeof( article.over ) ); | |
170 replyToFound = FALSE; | |
171 | |
172 /* Grab header lines first, getting overview info as we go. */ | |
173 while ( ( p = Utl_getHeaderLn( line, p ) ) != NULL | |
174 && line[ 0 ] != '\0' | |
175 && Prt_getField( field, value, line ) ) | |
176 { | |
177 /* Look for headers we need to stash. */ | |
178 if ( strcmp( field, "subject" ) == 0 ) | |
179 { | |
180 Utl_cpyStr( article.over.subject, value ); | |
181 DynStr_appLn( s, line ); | |
182 } | |
183 else if ( strcmp ( field, "from" ) == 0 ) | |
184 { | |
185 Utl_cpyStr( article.over.from, value ); | |
186 DynStr_appLn( s, line ); | |
187 } | |
188 else if ( strcmp ( field, "date" ) == 0 ) | |
189 { | |
190 Utl_cpyStr( article.over.date, value ); | |
191 DynStr_appLn( s, line ); | |
192 } | |
193 else if ( strcmp ( field, "references" ) == 0 ) | |
194 { | |
195 Utl_cpyStr( article.over.ref, value ); | |
196 DynStr_appLn( s, line ); | |
197 } | |
198 else if ( strcmp ( field, "message-id" ) == 0 ) | |
199 Utl_cpyStr( article.over.msgId, value ); | |
200 else if ( strcmp ( field, "newsgroups" ) == 0 ) | |
201 { | |
202 article.newsgroups = new_Itl( value, " ,\n\t" ); | |
203 DynStr_appLn( s, line ); | |
204 } | |
205 else if ( strcmp ( field, "control" ) == 0 ) | |
206 { | |
207 article.control = new_Itl( value, " " ); | |
208 DynStr_appLn( s, line ); | |
209 } | |
210 else if ( strcmp ( field, "reply-to" ) == 0 ) | |
211 { | |
212 replyToFound = TRUE; | |
213 DynStr_appLn( s, line ); | |
214 } | |
215 else if ( strcmp ( field, "x-sender" ) == 0 ) | |
216 { | |
217 DynStr_app( s, "X-NOFFLE-X-Sender: " ); | |
218 DynStr_appLn( s, value ); | |
219 } | |
220 else if ( strcmp ( field, "xref" ) == 0 ) | |
221 Log_inf( "Xref header in post ignored" ); | |
222 else | |
223 DynStr_appLn( s, line ); | |
224 } | |
225 | |
226 /* Now sort header-related issues */ | |
227 if ( article.over.from[ 0 ] == '\0' ) | |
228 { | |
229 Log_err( "Posted message has no From field" ); | |
230 return FALSE; | |
231 } | |
232 if ( article.over.subject[ 0 ] == '\0' ) | |
233 { | |
234 Log_err( "Posted message has no Subject field" ); | |
235 return FALSE; | |
236 } | |
237 if ( article.newsgroups == NULL || Itl_count( article.newsgroups) == 0 ) | |
238 { | |
239 Log_err( "Posted message has no valid Newsgroups field" ); | |
240 return FALSE; | |
241 } | |
242 if ( Cfg_replaceMsgId() ) | |
243 { | |
244 Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); | |
245 Log_dbg( "Replacing Message-ID with '%s'", article.over.msgId ); | |
246 } | |
247 else if ( article.over.msgId[ 0 ] == '\0' ) | |
248 { | |
249 Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); | |
250 Log_inf( "Adding missing Message-ID '%s'", article.over.msgId ); | |
251 } | |
252 else if ( ! Prt_isValidMsgId( article.over.msgId ) ) | |
253 { | |
254 Log_ntc( "Replacing invalid Message-ID '%s'", article.over.msgId ); | |
255 Prt_genMsgId( article.over.msgId, article.over.from, "NOFFLE" ); | |
256 } | |
257 DynStr_app( s, "Message-ID: " ); | |
258 DynStr_appLn( s, article.over.msgId ); | |
259 if ( ! replyToFound ) | |
260 { | |
261 Log_dbg( "Adding Reply-To field to posted message." ); | |
262 DynStr_app( s, "Reply-To: " ); | |
263 DynStr_appLn( s, article.over.from ); | |
264 } | |
265 if ( article.over.date[ 0 ] == '\0' ) | |
266 { | |
267 time_t t; | |
268 | |
269 time( &t ); | |
270 Utl_rfc822Date( t, article.over.date ); | |
271 DynStr_app( s, "Date: " ); | |
272 DynStr_appLn( s, article.over.date ); | |
273 } | |
274 if ( p == NULL || p[ 0 ] == '\0' ) | |
275 { | |
276 Log_err( "Posted message has no body" ); | |
277 return FALSE; | |
278 } | |
279 | |
280 /* Add the empty line separating header and body */ | |
281 DynStr_appLn( s, "" ); | |
282 | |
283 /* Now pop on the rest of the body and count the lines & bytes */ | |
284 DynStr_app( s, p ); | |
285 for ( p++, article.over.lines = 0; *p != '\0'; p++ ) | |
286 if ( *p == '\n' ) | |
287 article.over.lines++; | |
288 article.over.bytes = DynStr_len( s ); | |
289 | |
290 return TRUE; | |
291 } | |
292 | |
293 /* Add article to outgoing if needs be */ | |
294 static Bool | |
295 postExternal( void ) | |
296 { | |
297 if ( article.server == NULL ) | |
298 return TRUE; | |
299 | |
300 if ( ! Out_add( article.server, article.over.msgId, article.text ) ) | |
301 { | |
302 Log_err( "Cannot add posted article to outgoing directory" ); | |
303 return FALSE; | |
304 } | |
305 | |
306 return TRUE; | |
307 } | |
308 | |
309 /* Cancel and return TRUE if need to send cancel message on to server. */ | |
310 static Bool | |
311 controlCancel( const char *cancelId ) | |
312 { | |
313 return ( Ctrl_cancel( cancelId ) == CANCEL_NEEDS_MSG ); | |
314 } | |
315 | |
316 /* | |
317 It's a control message. Currently we only know about 'cancel' | |
318 messages; others are passed on for outside groups, and logged | |
319 as ignored for local groups. | |
320 */ | |
321 static Bool | |
322 handleControl( void ) | |
323 { | |
324 const char *grp; | |
325 const char *op; | |
326 | |
327 op = Itl_first( article.control ); | |
328 if ( op == NULL ) | |
329 { | |
330 Log_err( "Malformed control line." ); | |
331 return TRUE; | |
332 } | |
333 else if ( strcasecmp( op, "cancel" ) == 0 ) | |
334 { | |
335 if ( ! controlCancel( Itl_next( article.control ) ) ) | |
336 return TRUE; /* Handled entirely locally */ | |
337 } | |
338 else | |
339 { | |
340 /* Log 'can't do' for internal groups. */ | |
341 for( grp = Itl_first( article.newsgroups ); | |
342 grp != NULL; | |
343 grp = Itl_next( article.newsgroups ) ) | |
344 { | |
345 if ( Grp_exists( grp ) && Grp_local( grp ) ) | |
346 Log_inf( "Ignoring control '%s' for '%s'.", op, grp ); | |
347 } | |
348 } | |
349 | |
350 return postExternal(); | |
351 } | |
352 | |
353 static Bool | |
354 postArticle( Bool localOnly ) | |
355 { | |
356 const char *grp; | |
357 Bool err; | |
358 Bool postLocal; | |
359 Bool local; | |
360 | |
361 err = FALSE; | |
362 postLocal = Cfg_postLocal(); | |
363 | |
364 /* Run round & post locally */ | |
365 for( grp = Itl_first( article.newsgroups ); | |
366 grp != NULL; | |
367 grp = Itl_next( article.newsgroups ) ) | |
368 { | |
369 local = Grp_local( grp ); | |
370 if ( localOnly && ! local ) | |
371 continue; | |
372 if ( ( local || postLocal ) | |
373 && ( Grp_postAllow( grp ) == 'y' || localOnly ) ) | |
374 err = addToGroup( grp ) && err; | |
375 } | |
376 | |
377 if ( localOnly ) | |
378 return err; | |
379 else | |
380 return postExternal() && err; | |
381 } | |
382 | |
383 /* Register an article for posting. */ | |
384 Bool | |
385 Post_open( const char * text ) | |
386 { | |
387 if ( article.text != NULL ) | |
388 { | |
389 Log_err( "Busy article in Post_open." ); | |
390 return FALSE; | |
391 } | |
392 | |
393 if ( ! getArticleText( text ) ) | |
394 return FALSE; | |
395 | |
396 if ( Db_contains( article.over.msgId ) ) | |
397 { | |
398 Log_err( "Duplicate article %s.", article.over.msgId ); | |
399 return FALSE; | |
400 } | |
401 | |
402 return TRUE; | |
403 } | |
404 | |
405 /* Process the posting */ | |
406 Bool | |
407 Post_post( Bool localOnly ) | |
408 { | |
409 if ( ! checkPostableNewsgroup( localOnly ) ) | |
410 return FALSE; | |
411 | |
412 return ( article.control == NULL ) | |
413 ? ! postArticle( localOnly ) | |
414 : ! handleControl(); | |
415 } | |
148 | 416 |
149 /* Done with article - tidy up. */ | 417 /* Done with article - tidy up. */ |
150 void | 418 void |
151 Post_close( void ) | 419 Post_close( void ) |
152 { | 420 { |
153 article.text = NULL; | 421 if ( article.text != NULL ) |
422 { | |
423 del_DynStr( article.text ); | |
424 article.text = NULL; | |
425 } | |
426 if ( article.newsgroups != NULL ) | |
427 { | |
428 del_Itl( article.newsgroups ); | |
429 article.newsgroups = NULL; | |
430 } | |
431 if ( article.control != NULL ) | |
432 { | |
433 del_Itl( article.control ); | |
434 article.control = NULL; | |
435 } | |
154 article.posted = FALSE; | 436 article.posted = FALSE; |
155 } | 437 article.server = NULL; |
156 | 438 } |
439 |