1: #include "parms.h" 2: #include "structs.h" 3: #include "newsgate.h" 4: 5: #ifdef RCSIDENT 6: static char *RCSid = "$Header: newsdump.c,v 1.7.0.10 85/10/31 09:03:40 notes Rel $"; 7: #endif RCSIDENT 8: 9: /* 10: * newsnote - take a note and dump it in a format that news will 11: * understand. Submit the article to the news program as 12: * defined in newsgate.h 13: * 14: * newsresp - similar to newsnote, but it dumps a response instead. 15: * 16: * The routines build some title lines and other headers for 17: * submission to the news insertion program. The rest of the 18: * article is fed to the news program through a pipe. 19: * This turned out to be mucho mucho easier than building the 20: * properly formatted intersystem files. 21: * 22: * Original Coding: Ray Essick April 1982 23: * Modified to produce cleaner looking output: 24: * Ray Essick May 1, 1982 25: * (with good help from Brian Redman (harpo!ber) 26: * Modified to solve meta-character ("'`) problems in popen() 27: * Ray Essick September 1982 28: * 29: * Lots of code by Tw Cook at Hewlett-Packard to implement 30: * compatibility with the USENET standards for news. 31: * January 1984. 32: */ 33: 34: extern char *mnames[]; /* month names */ 35: FILE * popen (); 36: 37: char *Version = "$Revision: 1.7.0.10 $"; /* Use RCS info */ 38: 39: newsnote (io, note, notenum, ngroup, backwards) 40: struct io_f *io; 41: struct note_f *note; 42: char *ngroup; /* to this newsgroup */ 43: { 44: FILE * rnews; /* rnews pipe */ 45: char cmdline[CMDLEN]; /* command line */ 46: char line[TITLEN + 50]; /* scratch line */ 47: int i; 48: char *p, 49: *q; 50: char ntitle[TITLEN + 10]; /* title */ 51: char from[SYSSZ + NAMESZ + 3]; /* formatted author */ 52: char path[BUFSIZ]; /* path to author */ 53: char *author; /* for Path line */ 54: 55: /* 56: * format some of the internal data structures in a suitable 57: * format to send to the news system. 58: */ 59: 60: strcpy (ntitle, note -> ntitle); /* make a title */ 61: if (backwards) 62: { 63: strcat (ntitle, " "); /* separate */ 64: strcat (ntitle, NFSUFFIX); /* add suffix */ 65: } 66: 67: /* 68: * From: 69: * author information 70: */ 71: sprintf (from, "%s@%s", /* author */ 72: note -> n_auth.aname, note -> n_auth.asystem); 73: #ifdef notdef 74: /* 75: * decided it was better to NOT append a domain here... 76: */ 77: if (index (note -> n_auth.asystem, '.') != (char *) NULL)/* domained */ 78: { 79: sprintf (from, "%s@%s", 80: note -> n_auth.aname, note -> n_auth.asystem); 81: } 82: else 83: { 84: sprintf (from, "%s@%s.%s", 85: note -> n_auth.aname, note -> n_auth.asystem, 86: DFLTDOMAIN); /* append default */ 87: } 88: #endif notdef 89: /* 90: * Path: 91: * where the article has been 92: * Maybe we want to use some format besides a!b!c, something like 93: * a,b,c 94: */ 95: if (!strcmp (System, note -> n_id.sys)) /* local */ 96: { 97: sprintf (path, "%s", note -> n_id.sys); 98: } 99: else 100: { 101: if (!strcmp (note -> n_id.sys, note -> n_from)) /* one hop */ 102: { 103: sprintf (path, "%s!%s", 104: System, 105: note -> n_id.sys); 106: } 107: else 108: { 109: sprintf (path, "%s!%s!%s", /* several hops */ 110: System, note -> n_from, note -> n_id.sys); 111: } 112: } 113: #ifdef UGLYPATH /* usually so */ 114: /* 115: * Fill in the "authorname" with either a placeholder 116: * or the real author name. Key this off whether the unique 117: * id matches the author's home system. 118: * 119: * This will usually use AUTHORPLACEHOLDER on new systems 120: * since the unique id's don't have the domains in yet. 121: * 122: * hacked at to satisfy the public at large still using the 123: * Path line although it's not supposed to be good any longer. 124: * new code checks to see if things are a common prefix. and 125: * that the next character in the longer string is a "." 126: * if so, it declares things good enough and uses the real author. 127: */ 128: { 129: int idlen, 130: authlen; 131: int isok; 132: int minlen; 133: 134: idlen = strlen (note -> n_id.sys); 135: authlen = strlen (note -> n_auth.asystem); 136: minlen = idlen < authlen ? idlen : authlen; /* get shortest */ 137: isok = (strncmp (note -> n_id.sys, note -> n_auth.asystem, minlen) == 0); 138: if (isok && idlen < authlen) /* id is short */ 139: { 140: isok &= (note -> n_auth.asystem[idlen] == '.'); 141: } 142: else 143: { 144: if (isok && authlen < idlen) 145: isok &= (note -> n_id.sys[authlen] == '.'); 146: } 147: if (isok) 148: author = note -> n_auth.aname; /* ok for him */ 149: else 150: author = AUTHORPLACEHOLDER; /* not local */ 151: } 152: #else !UGLYPATH 153: /* 154: * the bnews code all assumes that the last component of 155: * the Path line is a user name. If we don't append something, 156: * that system will see the articles again, since news will 157: * disregard it when comparing to see where things have been. 158: */ 159: author = AUTHORPLACEHOLDER; 160: #endif UGLYPATH 161: /* 162: * dump the article 163: */ 164: 165: if ((rnews = popen (rnewscmd, "w")) == NULL) /* open news pipe */ 166: return (-1); /* report failure */ 167: 168: fprintf (rnews, "Relay-Version: Notesfiles %s; site %s\n", 169: Version, System); 170: /* 171: * Here we make a slight deviation from what one would expect. 172: * We use the LOCAL version of the notes/news gateway to pick 173: * the Posting Version line. This is because we are the version 174: * that actually makes the insertion into the news system. 175: * 176: * To back it up, the Usenet Standards papr (rfc whatever) says 177: * that the Posting-version "identifies the software responsible 178: * for entering this message into the network". 179: * Whether the "site" field should be the gatewaying site or where 180: * the article originated is a good question. 181: * I chose to make it the notes->news gateway running locally and 182: * the site where the article originated. 183: * 184: * The stuff with the #ifdef notdef is to preserve the old code 185: * that just labeled article we gateway with a Posting version 186: * of "Notesfiles" instead of "Notesfiles 1.x". 187: */ 188: #ifdef notdef 189: if (!strcmp (System, note -> n_id.sys)) /* local note */ 190: #endif notdef 191: { 192: /* 193: * always consider ourselves the posting version. We are the 194: * site that posted it to news! There could be a question 195: * about articles gated in two places or which site should be 196: * there. 197: */ 198: fprintf (rnews, "Posting-Version: Notesfiles %s; site %s\n", 199: Version, note -> n_id.sys); 200: } 201: #ifdef notdef 202: else /* remote note */ 203: fprintf (rnews, "Posting-Version: Notesfiles; site %s.%s\n", 204: note -> n_id.sys, DFLTDOMAIN); /* unknown version */ 205: #endif notdef 206: fprintf (rnews, "From: %s\n", from); /* formatted */ 207: /* 208: * Sample format that is legal 209: * fprintf (rnews, "Date: 13-Jan-83 12:08 PST\n"); 210: */ 211: fprintf (rnews, "Date: %02d-%3s-%02d %02d:%02d %3s\n", 212: note -> n_date.w_day, 213: mnames[note -> n_date.w_month], 214: note -> n_date.w_year - 1900, 215: note -> n_date.w_hours, 216: note -> n_date.w_mins, 217: tzone (¬e -> n_date)); 218: fprintf (rnews, "Newsgroups: %s\n", ngroup); 219: fprintf (rnews, "Subject: %s\n", ntitle); 220: fprintf (rnews, "Message-ID: <%ld@%s>\n", note -> n_id.uniqid, 221: note -> n_id.sys); 222: fprintf (rnews, "Path: %s!%s\n", path, author); 223: 224: /* 225: * send out the notesfile specfic headers 226: */ 227: if (note -> n_addr.addr == 0) /* make sure */ 228: note -> n_addr.textlen = 0; /* on empty text */ 229: 230: fprintf (rnews, "%s: #N:%s:%ld:%03o:%ld\n", /* nf header */ 231: NFLINE1, /* "NF-line1" */ 232: note -> n_id.sys, note -> n_id.uniqid, 233: note -> n_stat, ((long) note -> n_addr.textlen)); 234: 235: fprintf (rnews, "%s: %s!%s %3s %2d %02d:%02d:00 %4d\n", 236: NFLINE2, /* "Nf-line2" */ 237: note -> n_auth.asystem, note -> n_auth.aname,/* author */ 238: mnames[note -> n_date.w_month], /* date written */ 239: note -> n_date.w_day, 240: note -> n_date.w_hours, 241: note -> n_date.w_mins, 242: note -> n_date.w_year); 243: /* 244: * Optional headers that we don't hassle with right now: 245: * Organization: make a table driven routine to grab 246: */ 247: 248: putc ('\n', rnews); /* separator */ 249: if (backwards) /* include old stuff */ 250: { 251: fprintf (rnews, "#N:%s:%ld:%03o:%ld\n", /* nf header */ 252: note -> n_id.sys, note -> n_id.uniqid, 253: note -> n_stat, ((long) note -> n_addr.textlen)); 254: 255: fprintf (rnews, "%s!%s %3s %2d %02d:%02d:00 %4d\n", 256: note -> n_auth.asystem, note -> n_auth.aname,/* author */ 257: mnames[note -> n_date.w_month], /* date written */ 258: note -> n_date.w_day, 259: note -> n_date.w_hours, 260: note -> n_date.w_mins, 261: note -> n_date.w_year); 262: } 263: putc ('\n', rnews); /* separator */ 264: pageout (io, ¬e -> n_addr, rnews); /* dump text */ 265: fprintf (rnews, "\n"); /* ensure newline */ 266: pclose (rnews); /* close it */ 267: sleep (SLEEPTIME); /* wait a while */ 268: return (0); 269: } 270: 271: /* 272: * newsresp 273: * 274: * Dump a response to the news system. 275: */ 276: 277: newsresp (io, note, notenum, rsprec, roffset, respnum, ngroup, backwards) 278: struct io_f *io; 279: struct note_f *note; 280: struct resp_f *rsprec; 281: char *ngroup; 282: { 283: char cmdline[CMDLEN]; /* leggo brand */ 284: char line[TITLEN + 50]; /* scratch */ 285: FILE * rnews; 286: int i; 287: char *p, 288: *q; 289: char ntitle[TITLEN + 20]; /* formatted title */ 290: char from[SYSSZ + NAMESZ + 3]; /* formatted author */ 291: char path[BUFSIZ]; /* path to author */ 292: char *author; /* for Path: */ 293: long uniquenum; 294: 295: /* 296: * pre-format a few fields like titles, author information, 297: * paths to authors, and that sort of gunk. 298: */ 299: 300: ntitle[0] = '\0'; /* empty string */ 301: if (strncmp (note -> ntitle, "Re:", 3) && /* is it already */ 302: strncmp (note -> ntitle, "RE:", 3) && /* a response-like */ 303: strncmp (note -> ntitle, "re:", 3)) /* title? */ 304: { /* flag it as a */ 305: strcat (ntitle, "Re: "); /* response */ 306: } 307: strcat (ntitle, note -> ntitle); /* include title */ 308: if (backwards) 309: { 310: strcat (ntitle, " "); 311: strcat (ntitle, NFSUFFIX); /* include old */ 312: } 313: 314: /* 315: * From: 316: * author information 317: */ 318: sprintf (from, "%s@%s", /* author */ 319: rsprec -> r_auth[roffset].aname, rsprec -> r_auth[roffset].asystem); 320: #ifdef notdef 321: /* 322: * decided it was better to NOT append a domain at this point 323: */ 324: if (index (rsprec -> r_auth[roffset].asystem, '.') != (char *) NULL) 325: { /* already domained */ 326: sprintf (from, "%s@%s", 327: rsprec -> r_auth[roffset].aname, rsprec -> r_auth[roffset].asystem); 328: } 329: else 330: { 331: sprintf (from, "%s@%s.%s", 332: rsprec -> r_auth[roffset].aname, rsprec -> r_auth[roffset].asystem, 333: DFLTDOMAIN); 334: } 335: #endif notdef 336: /* 337: * Path: 338: * Where the article has been 339: */ 340: if (!strcmp (System, rsprec -> r_id[roffset].sys)) /* local */ 341: { 342: sprintf (path, "%s", rsprec -> r_id[roffset].sys); 343: } 344: else 345: { 346: if (!strcmp (rsprec -> r_id[roffset].sys, rsprec -> r_from[roffset])) 347: { /* one hop */ 348: sprintf (path, "%s!%s", 349: System, 350: rsprec -> r_id[roffset].sys); 351: } 352: else 353: { 354: sprintf (path, "%s!%s!%s", /* several hops */ 355: System, 356: rsprec -> r_from[roffset], 357: rsprec -> r_id[roffset].sys); 358: } 359: } 360: #ifdef UGLYPATH /* usually so */ 361: /* 362: * See if we can use the author's name instead of 363: * AUTHORPLACEHOLDER. Check this by comparin unique id's 364: * and the author's system. 365: * 366: * see similar code above in newsnote() for explanation of the 367: * gyrations here. 368: */ 369: { 370: int idlen, 371: authlen; 372: int isok; 373: int minlen; 374: 375: idlen = strlen (rsprec -> r_id[roffset].sys); 376: authlen = strlen (rsprec -> r_auth[roffset].asystem); 377: minlen = idlen < authlen ? idlen : authlen; 378: isok = (strncmp (rsprec -> r_id[roffset].sys, rsprec -> r_auth[roffset].asystem, minlen) == 0); 379: if (isok && idlen < authlen) /* id is short */ 380: { 381: isok &= (rsprec -> r_auth[roffset].asystem[idlen] == '.'); 382: } 383: else 384: { 385: if (isok && authlen < idlen) /* author syste is short */ 386: isok &= (rsprec -> r_id[roffset].sys[authlen] == '.'); 387: } 388: if (isok) 389: author = rsprec -> r_auth[roffset].aname; 390: else 391: author = AUTHORPLACEHOLDER; 392: } 393: #else !UGLYPATH 394: /* 395: * since bnews programs all assume the last component of the 396: * path line is a user name, we have to put something there 397: * to keep them from disregarding the system name when trying 398: * to see where the article has been. 399: */ 400: author = AUTHORPLACEHOLDER; 401: #endif UGLYPATH 402: 403: /* 404: * Time to send the article to the news system 405: */ 406: 407: if ((rnews = popen (rnewscmd, "w")) == NULL) /* will it work */ 408: return (-1); /* report failure */ 409: 410: fprintf (rnews, "Relay-Version: Notesfiles %s; site %s\n", 411: Version, System); 412: #ifdef notdef 413: if (!strcmp (System, rsprec -> r_id[roffset].sys)) /* local note */ 414: #endif notdef 415: { 416: /* 417: * always consider ourselves the posting version. We are the 418: * site that posted it to news! There could be a question 419: * about articles gated in two places or which site should be 420: * there. 421: * 422: * See comments around similar lines within the newsnote() 423: * routine above 424: */ 425: fprintf (rnews, "Posting-Version: Notesfiles %s; site %s\n", 426: Version, rsprec -> r_id[roffset].sys); 427: } 428: #ifdef notdef 429: else /* remote note */ 430: fprintf (rnews, "Posting-Version: Notesfiles; site %s.%s\n", 431: rsprec -> r_id[roffset].sys, DFLTDOMAIN);/* unknown version */ 432: #endif notdef 433: fprintf (rnews, "From: %s\n", from); /* formatted */ 434: /* 435: * Sample format that is legal 436: * fprintf (rnews, "Date: 13-Jan-83 12:08 PST\n"); 437: */ 438: fprintf (rnews, "Date: %02d-%3s-%02d %02d:%02d %3s\n", 439: rsprec -> r_when[roffset].w_day, 440: mnames[rsprec -> r_when[roffset].w_month], 441: rsprec -> r_when[roffset].w_year - 1900, 442: rsprec -> r_when[roffset].w_hours, 443: rsprec -> r_when[roffset].w_mins, 444: tzone (&rsprec -> r_when[roffset])); 445: fprintf (rnews, "Newsgroups: %s\n", ngroup); 446: fprintf (rnews, "Subject: %s\n", ntitle); 447: fprintf (rnews, "Message-ID: <%ld@%s>\n", 448: rsprec -> r_id[roffset].uniqid, rsprec -> r_id[roffset].sys); 449: fprintf (rnews, "Path: %s!%s\n", path, author); 450: 451: /* 452: * send out the notesfile specfic headers 453: */ 454: fprintf (rnews, "%s: #R:%s:%ld:%s:%ld:%03o:%ld\n", 455: NFLINE1, /* Nf-ID */ 456: note -> n_id.sys, note -> n_id.uniqid, 457: rsprec -> r_id[roffset].sys, 458: rsprec -> r_id[roffset].uniqid, 459: rsprec -> r_stat[roffset], 460: ((long) rsprec -> r_addr[roffset].textlen));/* force it long */ 461: 462: 463: fprintf (rnews, "%s: %s!%s %3s %2d %02d:%02d:00 %4d\n", 464: NFLINE2, /* NF-From */ 465: rsprec -> r_auth[roffset].asystem, rsprec -> r_auth[roffset].aname, 466: mnames[rsprec -> r_when[roffset].w_month], /* date written */ 467: rsprec -> r_when[roffset].w_day, 468: rsprec -> r_when[roffset].w_hours, 469: rsprec -> r_when[roffset].w_mins, 470: rsprec -> r_when[roffset].w_year); 471: /* 472: * we used to negate and multiply by 100 news ids. this was tied 473: * to the fact that news and notes id's conflicted. 474: * the new code to handle the arbitrary string message ids uses 475: * negative numbers to store the length of the original id. 476: * we change the following if from <0 to <-100 because 477: * we know lengths are likely < 100 and that the old flipped 478: * format is definitely <= -100. 479: */ 480: if ((uniquenum = note -> n_id.uniqid) <= -100) /* re-invert news */ 481: uniquenum /= (-100); /* back to normal */ 482: if (uniquenum >= 0) /* normal form */ 483: { 484: fprintf (rnews, "References: <%ld@%s>\n", 485: uniquenum, note -> n_id.sys); 486: } 487: else 488: { /* all in n_id.sys */ 489: /* 490: * print it if we have the entire id. if we only have a part, 491: * punt and that's too bad. 492: */ 493: if (-uniquenum == strlen (note -> n_id.sys)) /* have it all */ 494: fprintf (rnews, "References: %s\n", note -> n_id.sys); 495: } 496: 497: putc ('\n', rnews); /* separator */ 498: if (backwards) /* old stuff included */ 499: { 500: fprintf (rnews, "#R:%s:%ld:%s:%ld:%03o:%ld\n", 501: note -> n_id.sys, note -> n_id.uniqid, 502: rsprec -> r_id[roffset].sys, 503: rsprec -> r_id[roffset].uniqid, 504: rsprec -> r_stat[roffset], 505: ((long) rsprec -> r_addr[roffset].textlen));/* force it long */ 506: 507: fprintf (rnews, "%s!%s %3s %2d %02d:%02d:00 %4d\n", 508: rsprec -> r_auth[roffset].asystem, rsprec -> r_auth[roffset].aname, 509: mnames[rsprec -> r_when[roffset].w_month],/* date written */ 510: rsprec -> r_when[roffset].w_day, 511: rsprec -> r_when[roffset].w_hours, 512: rsprec -> r_when[roffset].w_mins, 513: rsprec -> r_when[roffset].w_year); 514: } 515: putc ('\n', rnews); /* separator */ 516: pageout (io, &rsprec -> r_addr[roffset], rnews); /* dump text */ 517: fprintf (rnews, "\n"); /* ensure a newline */ 518: pclose (rnews); /* close it */ 519: sleep (SLEEPTIME); /* and wait */ 520: return (0); 521: } 522: 523: /* 524: * A quick and cheap way to calculate a timezone. 525: * 526: * This routine makes the assumption that a notesfile site 527: * we gateway for is in the same timezone as our site. 528: * 529: */ 530: 531: char *tzone (when) 532: struct when_f when; /* unused */ 533: { 534: #ifdef BSD42 535: #include <sys/time.h> /* it moved! */ 536: #else 537: #include <time.h> 538: #endif BSD42 539: #include <sys/types.h> /* for ftime */ 540: 541: struct tm *ltime; 542: extern struct tm *localtime (); 543: long timenow; 544: 545: #ifdef USG 546: struct tm *bp; 547: extern char *tzname[]; 548: 549: time (&timenow); 550: ltime = localtime (&timenow); 551: return (tzname[ltime -> tm_isdst]); 552: #else 553: /* 554: * for systems that still have the "timezone" function from V7 555: */ 556: #include <sys/timeb.h> /* for ftime */ 557: struct timeb rawtime; 558: extern char *timezone (); /* please lint */ 559: 560: ftime (&rawtime); /* get information */ 561: time (&timenow); /* get now */ 562: ltime = localtime (&timenow); /* see if DST */ 563: return (timezone ((int) rawtime.timezone, ltime -> tm_isdst)); 564: #endif 565: }