10 #include <arpa/inet.h>
13 #include <netinet/in.h>
17 #include "nntp-merge.h"
19 static int output64(FILE *file, unsigned long v, int minn) {
20 static const char *const b64=
21 "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "+-";
24 putc(b64[v&077],file);
31 static void eatpost() {
32 if (!copydatafile(stdin,0)) die("failed read from client during POST");
35 static void badpost(const char *why) {
37 printf("441 not acceptable: %s\r\n",why);
40 static int attemptposting(struct serverinfo *si, FILE *file,
41 char response[MAX_RESPONSE+3]) {
45 if (fseek(file,0,SEEK_SET)) {
46 sprintf(response,"503 rewind article failed: %.100s",strerror(errno));
49 rcode= servercommand(si,"POST",response,0);
50 if (rcode != 0x340) return rcode;
51 while ((c= getc(file)) != EOF) {
52 if (putc(c,si->wfile) == EOF) {
53 sprintf(response,"503 server write failed: %.100s",strerror(errno));
54 closeserver(si); return 0x503;
57 if (fflush(si->wfile) == EOF || ferror(file)) {
58 sprintf(response,"503 read article failed: %.100s",strerror(errno));
59 closeserver(si); return 0x503;
61 if (!fgets(response,MAX_RESPONSE+3,si->rfile)) {
62 sprintf(response,"503 server read failed: %.100s",strerror(errno));
63 closeserver(si); return 0x503;
65 if (!stripcommand(response)) {
66 sprintf(response,"503 null garbage in reply to article");
67 closeserver(si); return 0x503;
69 if (!decoderesponse(response,&rv,si)) return 0x503;
73 void cmd_post(char *arg, const struct cmdinfo *cip) {
74 char linebuf[MAX_XREFLINE+3];
76 int n, m, hadmessageid, haddate, hadnewsgroups, moderated, zonediff, i, j, rcode;
77 int zonediffc, hadpermprob, c;
79 char *p, *colon, *space, *comma;
80 struct serverinfo *postto[20], *si;
81 char response[MAX_RESPONSE+3];
85 struct tm local, gm, *tmtmp;
87 file= tmpfile(); if (!file) die("unable to make tmpfile for POST");
89 fputs("340 go ahead.\r\n",stdout);
91 fputs("340 make my day, punk.\r\n",stdout);
92 if (ferror(stdout) || fflush(stdout)) die("unable to say go ahead to POST");
93 hadmessageid= haddate= hadnewsgroups= 0; hadpermprob= 0;
95 for (si= servers; si; si= si->next) si->tempfile= 0;
97 errno= 0; if (!fgets(linebuf,MAX_XREFLINE,stdin)) die("error reading data in POST");
98 m= n= strlen(linebuf); if (!n) die("null data line in POST");
99 while (n > 0 && (linebuf[n-1] == '\n' || linebuf[n-1] == '\r')) n--;
100 if (m == n) { badpost("line in POST too long"); fclose(file); return; }
103 if (n == 1 && linebuf[0] == '.') {
104 printf("441 message must have a body.\r\n"); fclose(file); return;
106 colon= strchr(linebuf,':');
107 if (!colon) { badpost("header line with no colon"); fclose(file); return; }
109 while (isspace(*colon)) colon++;
110 for (p=linebuf; *p; p++)
111 if (isspace(*p)) { badpost("space in header name"); fclose(file); return; }
112 if (!strcasecmp(linebuf,"originator")) continue;
113 if (!strcasecmp(linebuf,"nntp-posting-host")) continue;
114 if (!strcasecmp(linebuf,"path")) continue;
115 fprintf(file,"%s: %s\r\n",linebuf,colon);
116 if (!strcasecmp(linebuf,"message-id")) {
117 if (hadmessageid++) {
118 badpost("two (or more) Message-ID headers"); fclose(file); return;
120 } else if (!strcasecmp(linebuf,"date")) {
122 badpost("two (or more) Date headers"); fclose(file); return;
124 } else if (!strcasecmp(linebuf,"newsgroups")) {
125 if (hadnewsgroups++) {
126 badpost("two (or more) Newsgroups headers"); fclose(file); return;
130 comma= strchr(p,',');
131 if (comma) *comma++= 0;
133 if (stillrestricted(&gi->restrictto)) {
134 hadpermprob= 1; p= comma; continue;
135 } else if (!gi->postto[0]) {
139 i<(sizeof(gi->postto)/sizeof(gi->postto[0])) &&
143 j<(sizeof(postto)/sizeof(postto[0])) && postto[j] &&
144 postto[j] != gi->postto[i];
146 if (j >= (sizeof(postto)/sizeof(postto[0]))) {
147 badpost("would have to post to too many servers"); fclose(file); return;
150 postto[j]= gi->postto[i];
151 if (j+1 < (sizeof(postto)/sizeof(postto[0]))) postto[j+1]= 0;
154 /* We don't bother checking for moderation status if we're only
155 * posting to one group, and that group is only on one server.
157 if (p == colon && !comma && !gi->postto[1]) break;
158 rcode= servercommand(gi->postto[0],"LIST",response,0);
159 if (rcode != 0x215) {
160 eatpost(); fclose(file);
161 printf("441 couldn't LIST to check moderation status of %s - %s: %s\r\n",
162 p,gi->postto[0]->hostname,response);
167 if (!fgets(response,sizeof(response),gi->postto[0]->rfile))
168 serverdataerr(gi->postto[0]);
169 if (!strcmp(response,".\r\n") || !strcmp(response,".\n")) break;
170 space= strchr(response,' ');
171 if (!space) continue;
173 if (strcmp(response,p)) continue;
174 space= strchr(space,' ');
175 if (!space) continue;
176 space= strchr(++space,' ');
177 if (!space) continue;
179 if (*space == 'm') moderated= 1;
190 if (c != ' ' && c != '\t') break;
193 if (c == '\n') break;
201 /* Right, we've copied the header into tmpfile, and we've read
202 * but not yet copied the blank line. We must add the Originator
205 if (!hadnewsgroups) { badpost("missing Newsgroups header"); fclose(file); return; }
210 printf("480 the power of %s is but weak.\r\n",lastdoneauth);
212 printf("480 thou must prove thine strength.\r\n");
215 badpost("no server(s) for those groups, cannot post.\r\n");
219 id= ident_lookup(0,30);
220 fprintf(file,"Originator: %s@", id && id->identifier ? id->identifier : "");
221 if (theirfqdn) fprintf(file,"%s ([%s])",
222 strcmp(theirfqdn,"localhost") ? theirfqdn : myfqdn,
223 inet_ntoa(peername.sin_addr));
224 else fprintf(file,"[%s]", inet_ntoa(peername.sin_addr));
226 /* Forms of Originator line are:
227 * Originator: <identifier>@<hostname> ([<addr>])
228 * Originator: <identifier>@[<addr>]
229 * Originator: @<hostname> ([<addr>])
230 * Originator: @[addr]
231 * <opsys>, <charset> are empty strings if not available.
234 if (!haddate || !hadmessageid) {
238 tmtmp= gmtime(&now); if (!tmtmp) die("can't get Greenwich Mean Time");
240 tmtmp= localtime(&now); if (!tmtmp) die("can't get local time");
242 zonediff= (local.tm_hour*60 + local.tm_min) - (gm.tm_hour*60 + gm.tm_min);
246 if (zonediff > 720) { zonediff= 1440-zonediff; zonediffc= '-'; }
247 assert(sprintf(buf,"Date: %%d %%b %%Y %%H:%%M:%%S %c%02d%02d (%%Z)\r\n",
248 zonediffc,zonediff/60,zonediff%60) < sizeof(buf));
249 if (strftime(response,sizeof(response),buf,&local) == sizeof(response))
250 die("date is too long for buffer");
251 fputs(response,file);
254 unsigned long pid= getpid();
255 fputs("Message-ID: <",file);
256 output64(file,(now&03)|(pid<<2),0);
258 output64(file,now>>2,5);
259 fprintf(file,"@%s>\r\n",myfqdn);
264 if (!copydatafile(stdin,file)) die("failed read from client during POST");
266 if (ferror(file) || fflush(file) || fseek(file,0,SEEK_SET))
267 die("error writing tmp posting file");
269 rcode= attemptposting(postto[0],file,response);
270 if (rcode != 0x240) {
272 printf("441 POST failed - %s: %s\r\n",postto[0]->hostname,response);
275 printf("240 %s ok",postto[0]->hostname); fflush(stdout);
276 for (i=1; i<sizeof(postto)/sizeof(postto[0]) && postto[i]; i++) {
277 rcode= attemptposting(postto[i],file,response);
278 if (rcode != 0x240) {
279 printf("; %s err %.30s",postto[i]->hostname,response);
281 printf("; %s ok",postto[i]->hostname);
285 printf(" - posted.\r\n");