/**/ #include #include #include #include #include #include #include #include #include #include #include #include #include "nntp-merge.h" static int output64(FILE *file, unsigned long v, int minn) { static const char *const b64= "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "+-"; int i= 0; while (minn>0 || v) { putc(b64[v&077],file); v >>= 6; i++; minn--; } return i; } static void eatpost() { if (!copydatafile(stdin,0)) die("failed read from client during POST"); } static void badpost(const char *why) { eatpost(); printf("441 not acceptable: %s\r\n",why); } static int attemptposting(struct serverinfo *si, FILE *file, char response[MAX_RESPONSE+3]) { int rcode, c; unsigned long rv; if (fseek(file,0,SEEK_SET)) { sprintf(response,"503 rewind article failed: %.100s",strerror(errno)); return 0x503; } rcode= servercommand(si,"POST",response,0); if (rcode != 0x340) return rcode; while ((c= getc(file)) != EOF) { if (putc(c,si->wfile) == EOF) { sprintf(response,"503 server write failed: %.100s",strerror(errno)); closeserver(si); return 0x503; } } if (fflush(si->wfile) == EOF || ferror(file)) { sprintf(response,"503 read article failed: %.100s",strerror(errno)); closeserver(si); return 0x503; } if (!fgets(response,MAX_RESPONSE+3,si->rfile)) { sprintf(response,"503 server read failed: %.100s",strerror(errno)); closeserver(si); return 0x503; } if (!stripcommand(response)) { sprintf(response,"503 null garbage in reply to article"); closeserver(si); return 0x503; } if (!decoderesponse(response,&rv,si)) return 0x503; return rv; } void cmd_post(char *arg, const struct cmdinfo *cip) { char linebuf[MAX_XREFLINE+3]; FILE *file; int n, m, hadmessageid, haddate, hadnewsgroups, moderated, zonediff, i, j, rcode; int zonediffc, hadpermprob, c; struct groupinfo *gi; char *p, *colon, *space, *comma; struct serverinfo *postto[20], *si; char response[MAX_RESPONSE+3]; char buf[1000]; IDENT *id; time_t now; struct tm local, gm, *tmtmp; file= tmpfile(); if (!file) die("unable to make tmpfile for POST"); if (lastdoneauth) fputs("340 go ahead.\r\n",stdout); else fputs("340 make my day, punk.\r\n",stdout); if (ferror(stdout) || fflush(stdout)) die("unable to say go ahead to POST"); hadmessageid= haddate= hadnewsgroups= 0; hadpermprob= 0; postto[0]= 0; for (si= servers; si; si= si->next) si->tempfile= 0; for (;;) { errno= 0; if (!fgets(linebuf,MAX_XREFLINE,stdin)) die("error reading data in POST"); m= n= strlen(linebuf); if (!n) die("null data line in POST"); while (n > 0 && (linebuf[n-1] == '\n' || linebuf[n-1] == '\r')) n--; if (m == n) { badpost("line in POST too long"); fclose(file); return; } if (n==0) break; linebuf[n]= 0; if (n == 1 && linebuf[0] == '.') { printf("441 message must have a body.\r\n"); fclose(file); return; } colon= strchr(linebuf,':'); if (!colon) { badpost("header line with no colon"); fclose(file); return; } *colon++= 0; while (isspace(*colon)) colon++; for (p=linebuf; *p; p++) if (isspace(*p)) { badpost("space in header name"); fclose(file); return; } if (!strcasecmp(linebuf,"originator")) continue; if (!strcasecmp(linebuf,"nntp-posting-host")) continue; if (!strcasecmp(linebuf,"path")) continue; fprintf(file,"%s: %s\r\n",linebuf,colon); if (!strcasecmp(linebuf,"message-id")) { if (hadmessageid++) { badpost("two (or more) Message-ID headers"); fclose(file); return; } } else if (!strcasecmp(linebuf,"date")) { if (haddate++) { badpost("two (or more) Date headers"); fclose(file); return; } } else if (!strcasecmp(linebuf,"newsgroups")) { if (hadnewsgroups++) { badpost("two (or more) Newsgroups headers"); fclose(file); return; } p= colon; while (p) { comma= strchr(p,','); if (comma) *comma++= 0; gi= findgroup(p); if (stillrestricted(&gi->restrictto)) { hadpermprob= 1; p= comma; continue; } else if (!gi->postto[0]) { p= comma; continue; } for (i=0; i<(sizeof(gi->postto)/sizeof(gi->postto[0])) && gi->postto[i]; i++) { for (j=0; j<(sizeof(postto)/sizeof(postto[0])) && postto[j] && postto[j] != gi->postto[i]; j++); if (j >= (sizeof(postto)/sizeof(postto[0]))) { badpost("would have to post to too many servers"); fclose(file); return; } if (!postto[j]) { postto[j]= gi->postto[i]; if (j+1 < (sizeof(postto)/sizeof(postto[0]))) postto[j+1]= 0; } } /* We don't bother checking for moderation status if we're only * posting to one group, and that group is only on one server. */ if (p == colon && !comma && !gi->postto[1]) break; rcode= servercommand(gi->postto[0],"LIST",response,0); if (rcode != 0x215) { eatpost(); fclose(file); printf("441 couldn't LIST to check moderation status of %s - %s: %s\r\n", p,gi->postto[0]->hostname,response); return; } moderated= 0; for (;;) { if (!fgets(response,sizeof(response),gi->postto[0]->rfile)) serverdataerr(gi->postto[0]); if (!strcmp(response,".\r\n") || !strcmp(response,".\n")) break; space= strchr(response,' '); if (!space) continue; *space++= 0; if (strcmp(response,p)) continue; space= strchr(space,' '); if (!space) continue; space= strchr(++space,' '); if (!space) continue; ++space; if (*space == 'm') moderated= 1; } if (moderated) { postto[1]= 0; break; } p= comma; } } else { for (;;) { c= getchar(); if (c != ' ' && c != '\t') break; for (;;) { putc(c,file); if (c == '\n') break; c= getchar(); if (c == EOF) break; } } ungetc(c,stdin); } } /* Right, we've copied the header into tmpfile, and we've read * but not yet copied the blank line. We must add the Originator * field. */ if (!hadnewsgroups) { badpost("missing Newsgroups header"); fclose(file); return; } if (!postto[0]) { if (hadpermprob) { eatpost(); if (lastdoneauth) { printf("480 the power of %s is but weak.\r\n",lastdoneauth); } else { printf("480 thou must prove thine strength.\r\n"); } } else { badpost("no server(s) for those groups, cannot post.\r\n"); } return; } id= ident_lookup(0,30); fprintf(file,"Originator: %s@", id && id->identifier ? id->identifier : ""); if (theirfqdn) fprintf(file,"%s ([%s])", strcmp(theirfqdn,"localhost") ? theirfqdn : myfqdn, inet_ntoa(peername.sin_addr)); else fprintf(file,"[%s]", inet_ntoa(peername.sin_addr)); fputs("\r\n",file); /* Forms of Originator line are: * Originator: @ ([]) * Originator: @[] * Originator: @ ([]) * Originator: @[addr] * , are empty strings if not available. */ if (!haddate || !hadmessageid) { time(&now); } if (!haddate) { tmtmp= gmtime(&now); if (!tmtmp) die("can't get Greenwich Mean Time"); gm= *tmtmp; tmtmp= localtime(&now); if (!tmtmp) die("can't get local time"); local= *tmtmp; zonediff= (local.tm_hour*60 + local.tm_min) - (gm.tm_hour*60 + gm.tm_min); zonediff += 2880; zonediff %= 1440; zonediffc= '+'; if (zonediff > 720) { zonediff= 1440-zonediff; zonediffc= '-'; } assert(sprintf(buf,"Date: %%d %%b %%Y %%H:%%M:%%S %c%02d%02d (%%Z)\r\n", zonediffc,zonediff/60,zonediff%60) < sizeof(buf)); if (strftime(response,sizeof(response),buf,&local) == sizeof(response)) die("date is too long for buffer"); fputs(response,file); } if (!hadmessageid) { unsigned long pid= getpid(); fputs("Message-ID: <",file); output64(file,(now&03)|(pid<<2),0); putc('*',file); output64(file,now>>2,5); fprintf(file,"@%s>\r\n",myfqdn); sleep(2); } fputs("\r\n",file); if (!copydatafile(stdin,file)) die("failed read from client during POST"); if (ferror(file) || fflush(file) || fseek(file,0,SEEK_SET)) die("error writing tmp posting file"); rcode= attemptposting(postto[0],file,response); if (rcode != 0x240) { fclose(file); printf("441 POST failed - %s: %s\r\n",postto[0]->hostname,response); return; } printf("240 %s ok",postto[0]->hostname); fflush(stdout); for (i=1; ihostname,response); } else { printf("; %s ok",postto[i]->hostname); } fflush(stdout); } printf(" - posted.\r\n"); fclose(file); return; }