home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / nntp1.5 / part08 < prev    next >
Encoding:
Internet Message Format  |  1988-04-19  |  42.1 KB

  1. Subject:  v14i054:  Network News Transfer Protocol, version 1.5, Part08/09
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Phil Lapsley <phil@ucbvax.berkeley.edu>
  7. Posting-number: Volume 14, Issue 54
  8. Archive-name: nntp1.5/part08
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 8 (of 9)."
  17. # Contents:  ./support/nntp_awk.ucbvax ./xmit/nntpxmit.c
  18. # Wrapped by rsalz@fig.bbn.com on Tue Apr 19 18:16:49 1988
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f './support/nntp_awk.ucbvax' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'./support/nntp_awk.ucbvax'\"
  22. else
  23. echo shar: Extracting \"'./support/nntp_awk.ucbvax'\" \(12577 characters\)
  24. sed "s/^X//" >'./support/nntp_awk.ucbvax' <<'END_OF_FILE'
  25. X# an awk script 
  26. X# an NNTP log summary report generator
  27. X#
  28. X# NOTE: for systems that are not as yet using the new 4.3 BSD syslog
  29. X# (and therefore have nntp messages lumped with everything else), it
  30. X# would be best to invoke this script thusly:
  31. X#
  32. X#    egrep nntp syslog.old | awk -f nntp_awk > report_of_the_week
  33. X#
  34. X# because this script will include in the report all messages in the log
  35. X# that it does not recognize (on the assumption that they are errors to
  36. X# be dealt with by a human).
  37. X#
  38. X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
  39. X# May 17, 1986 - Norwegian Independence Day
  40. X#
  41. X# Recognize some new things - February 22, 1987
  42. X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
  43. X#
  44. X# fix "xmt is not an array" bug - March 11, 1987
  45. X# Change Elapsed/CPU fields to break out time values, HH:MM:SS
  46. X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
  47. X#
  48. X# Add reporting for newnews commands - August 27, 1987
  49. X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
  50. X#
  51. X# Add nntpxmit connection attempt counting/reporting - December 7, 1987
  52. X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
  53. X#
  54. BEGIN{
  55. X    readers = 0;
  56. X    transmit = 0;
  57. X    receive = 0;
  58. X    polled = 0;
  59. X}
  60. X### Skip stderr reports from rnews
  61. X{
  62. X    n = split($6, path, "/");
  63. X    if (path[n] == "rnews:") next;
  64. X    n = split($7, path, "/");
  65. X    if (path[n] == "rnews") next;
  66. X    host = $6;
  67. X}
  68. X$7 == "group" {
  69. X    readers = 1;
  70. X    ng[$8]++;
  71. X    next;
  72. X}
  73. X$7 == "ihave" {
  74. X    receive = 1;
  75. X    rec[host]++;
  76. X    if ($9 == "accepted") {
  77. X        rec_accept[host]++;
  78. X        if ($10 == "failed") rec_failed[host]++;
  79. X    } else if ($9 == "rejected") rec_refuse[host]++;
  80. X    next;
  81. X}
  82. X# this is from version 1.4 of nntpd
  83. X$7 == "ihave_stats" {
  84. X    receive = 1;
  85. X    rec[host] += $9 + $11 + $13;
  86. X    rec_accept[host] += $9;
  87. X    rec_refuse[host] += $11;
  88. X    rec_failed[host] += $13;
  89. X    next;
  90. X}
  91. X$7 == "connect" {
  92. X    systems[host]++;
  93. X    next;
  94. X}
  95. X# nntpxmit connection errors
  96. X# Ooooh! I *wish* awk had N dimensional arrays,
  97. X# so I wouldn't have to throw away the error message here!
  98. X$7 == "hello:" {
  99. X    conn[host]++;
  100. X    if ($8 == "Connection" && $9 == "refused")
  101. X        rmt_fail[host]++;
  102. X    else
  103. X        open_fail[host]++;
  104. X    next;
  105. X}
  106. X# we'll get stats from this, don't count conn[]
  107. X$7 == "xfer:" {
  108. X    open_fail[host]++;
  109. X# since these are expected to be few in number, we still print
  110. X# the exact error (no "next;" statement here).
  111. X}
  112. X$7 == "greeted" {
  113. X    conn[host]++;
  114. X    rmt_fail[host]++;
  115. X    next;
  116. X}
  117. X$7 == "host" && $8 == "unknown" {
  118. X    conn[host]++;
  119. X    ns_fail[host]++;
  120. X    next;
  121. X}
  122. X# nntpd connection abort - all "broken pipe" right now
  123. X$7 == "disconnect:" { next }
  124. X# syslogd shit
  125. X$7 == "repeated" { next }
  126. X# inews shit
  127. X$11 == "spooled" { next }
  128. X$7 == "exit" {
  129. X    if ($8 > 0) readers = 1;
  130. X    articles[host] += $8;
  131. X    groups[host] += $10;
  132. X    next;
  133. X}
  134. X$7 == "xmit" {
  135. X    xmt_cpu[host] += $9 + $11;
  136. X    xmt_ela[host] += $13;
  137. X    next;
  138. X}
  139. X$7 == "times" {
  140. X    cpu[host] += $9 + $11;
  141. X    ela[host] += $13;
  142. X    next;
  143. X}
  144. X$7 == "stats" {
  145. X    transmit = 1;
  146. X    conn[host]++;
  147. X    xmt[host] += $8;
  148. X    xmt_accept[host] += $10;
  149. X    xmt_refuse[host] += $12;
  150. X    xmt_failed[host] += $14;
  151. X    next;
  152. X}
  153. X#
  154. X#    For the Nth time, I wish awk had two dimensional associative
  155. X#    arrays. I assume that the last request is the same as all the
  156. X#    others in this section of logfile.
  157. X#
  158. X$7 == "newnews" {
  159. X    polled = 1;
  160. X    poll[host] ++;
  161. X    poll_asked[host] = $8;
  162. X    next;
  163. X}
  164. X$7 == "newnews_stats" {
  165. X    poll_offered[host] += $9;
  166. X    poll_took[host] += $11;
  167. X    next;
  168. X}
  169. X$7 == "post" {
  170. X    readers = 1;
  171. X    post[host]++;
  172. X    next;
  173. X}
  174. X$7 == "timeout" {
  175. X    timeout[host]++;
  176. X    timeouts = 1;
  177. X    next;
  178. X}
  179. X$7 == "unrecognized" {
  180. X    unknown[host] = 1;
  181. X    curious = 1;
  182. X}
  183. X### Print anything that we don't recognize in the report
  184. X{
  185. X    print;
  186. X}
  187. END{
  188. X    printf("\n");
  189. X###############################################################################
  190. X### Article Exchange With Peers (other servers) Statistics                  ###
  191. X###############################################################################
  192. X    if (transmit || receive || polled)
  193. X        printf("NNTP peer article transfers\n\n");
  194. X
  195. X    if (polled) for(s in poll) servers[s]++;
  196. X    if (receive) for(s in rec) servers[s]++;
  197. X    if (transmit) for(s in xmt) servers[s]++;
  198. X
  199. X    if (receive) {
  200. X        printf("Article Reception (they contact us)\n");
  201. X        printf("System                  Offered  Took  Toss  Fail Toss   Elapsed       CPU  Pct\n");
  202. X        for(s in rec) {
  203. X
  204. X            nrec += rec[s];
  205. X            nrec_accept += rec_accept[s];
  206. X            nrec_refuse += rec_refuse[s];
  207. X            nrec_failed += rec_failed[s];
  208. X            nrec_cpu += cpu[s];
  209. X            nrec_ela += ela[s];
  210. X
  211. X            they_offered = rec[s];
  212. X            if (they_offered == 0) they_offered = 1;
  213. X            we_toss = (rec_refuse[s] / they_offered) * 100 + 0.5;
  214. X
  215. X            e_hours      = ela[s] / 3600;
  216. X            e_sec        = ela[s] % 3600;
  217. X            e_min        = e_sec / 60;
  218. X            e_sec        %= 60;
  219. X
  220. X            c_hours      = cpu[s] / 3600;
  221. X            c_sec        = cpu[s] % 3600;
  222. X            c_min        = c_sec / 60;
  223. X            c_sec        %= 60;
  224. X
  225. X            tmp = ela[s];
  226. X            if (tmp == 0) tmp = 1;
  227. X            pct = ((cpu[s] / tmp) * 100.0 + 0.5);
  228. X
  229. X            printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, rec[s], rec_accept[s], rec_refuse[s], rec_failed[s], we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  230. X        }
  231. X
  232. X        e_hours      = nrec_ela / 3600;
  233. X        e_sec        = nrec_ela % 3600;
  234. X        e_min        = e_sec / 60;
  235. X        e_sec        %= 60;
  236. X
  237. X        c_hours      = nrec_cpu / 3600;
  238. X        c_sec        = nrec_cpu % 3600;
  239. X        c_min        = c_sec / 60;
  240. X        c_sec        %= 60;
  241. X
  242. X        they_offered = nrec;
  243. X        if (they_offered == 0) they_offered = 1;
  244. X        we_toss = (nrec_refuse / they_offered) * 100 + 0.5;
  245. X
  246. X        if (nrec_ela == 0) nrec_ela = 1;
  247. X        pct = ((nrec_cpu / nrec_ela) * 100.0 + 0.5);
  248. X
  249. X        printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nrec, nrec_accept, nrec_refuse, nrec_failed, we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  250. X    }
  251. X
  252. X###############################################################################
  253. X    if (polled) {
  254. X        printf("Article Transmission (they poll us)\n");
  255. X        printf("System                     Conn Offrd  Took   Elapsed       CPU  Pct  Groups\n");
  256. X        npoll = 0;
  257. X        npoll_offered = 0;
  258. X        npoll_took = 0;
  259. X        npoll_cpu = 0;
  260. X        npoll_ela = 0;
  261. X
  262. X        for(s in poll) {
  263. X            npoll += poll[s];
  264. X            npoll_offered += poll_offered[s];
  265. X            npoll_took += poll_took[s];
  266. X
  267. X            if (rec[s]) {
  268. X                printf("%-25s %5d %5d %5d  (see Article Reception)  %s\n", s, poll[s], poll_offered[s], poll_took[s], poll_asked[s]);
  269. X            } else {
  270. X                npoll_ela += ela[s];
  271. X                npoll_cpu += cpu[s];
  272. X
  273. X                e_hours = ela[s] / 3600;
  274. X                e_sec   = ela[s] % 3600;
  275. X                e_min   = e_sec / 60;
  276. X                e_sec   %= 60;
  277. X
  278. X                c_hours = cpu[s] / 3600;
  279. X                c_sec   = cpu[s] % 3600;
  280. X                c_min   = c_sec / 60;
  281. X                c_sec   %= 60;
  282. X
  283. X                tmp = ela[s];
  284. X                if (tmp == 0) tmp = 1;
  285. X                pct = ((cpu[s] / tmp) * 100.0 + 0.5);
  286. X
  287. X                printf("%-25s %5d %5d %5d %3d:%02d:%02d %3d:%02d:%02d %3d%%  %s\n", s, poll[s], poll_offered[s], poll_took[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct, poll_asked[s]);
  288. X            }
  289. X        }
  290. X        printf("\n%-25s %5d %5d %5d", "TOTALS", npoll, npoll_offered, npoll_took);
  291. X        if (npoll_ela > 0 && npoll_cpu > 0) {
  292. X
  293. X            e_hours = npoll_ela / 3600;
  294. X            e_sec   = npoll_ela % 3600;
  295. X            e_min   = e_sec / 60;
  296. X            e_sec   %= 60;
  297. X
  298. X            c_hours = npoll_cpu / 3600;
  299. X            c_sec   = npoll_cpu % 3600;
  300. X            c_min   = c_sec / 60;
  301. X            c_sec   %= 60;
  302. X
  303. X            tmp = npoll_ela;
  304. X            if (tmp == 0) tmp = 1;
  305. X            pct = ((npoll_cpu / tmp) * 100.0 + 0.5);
  306. X
  307. X            printf(" %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  308. X        } else
  309. X            printf("\n\n");
  310. X    }
  311. X
  312. X###############################################################################
  313. X    if (transmit) {
  314. X        printf("Article Transmission (we contact them)\n");
  315. X        printf("System                    Offrd  Took  Toss  Fail  Pct   Elapsed       CPU  Pct\n");
  316. X        for(s in xmt) {
  317. X            we_offered = xmt[s];
  318. X            if (we_offered == 0) we_offered = 1;
  319. X            they_toss = (xmt_refuse[s] / we_offered) * 100 + 0.5;
  320. X
  321. X            e_hours = xmt_ela[s] / 3600;
  322. X            e_sec   = xmt_ela[s] % 3600;
  323. X            e_min   = e_sec / 60;
  324. X            e_sec   %= 60;
  325. X
  326. X            c_hours = xmt_cpu[s] / 3600;
  327. X            c_sec   = xmt_cpu[s] % 3600;
  328. X            c_min   = c_sec / 60;
  329. X            c_sec   %= 60;
  330. X
  331. X            elapsed = xmt_ela[s];
  332. X            if (elapsed == 0) elapsed = 1;
  333. X            pct = ((xmt_cpu[s] / elapsed) * 100.0 + 0.5);
  334. X
  335. X            printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, xmt[s], xmt_accept[s], xmt_refuse[s], xmt_failed[s], they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  336. X
  337. X            nxmt        += xmt[s];
  338. X            nxmt_accept += xmt_accept[s];
  339. X            nxmt_refuse += xmt_refuse[s];
  340. X            nxmt_failed += xmt_failed[s];
  341. X            nxmt_ela    += xmt_ela[s];
  342. X            nxmt_cpu    += xmt_cpu[s];
  343. X        }
  344. X
  345. X        we_offered = nxmt;
  346. X        if (we_offered == 0) we_offered = 1;
  347. X        they_toss = (nxmt_refuse / we_offered) * 100 + 0.5;
  348. X
  349. X        e_hours = nxmt_ela / 3600;
  350. X        e_sec   = nxmt_ela % 3600;
  351. X        e_min   = e_sec / 60;
  352. X        e_sec   %= 60;
  353. X
  354. X        c_hours = nxmt_cpu / 3600;
  355. X        c_sec   = nxmt_cpu % 3600;
  356. X        c_min   = c_sec / 60;
  357. X        c_sec   %= 60;
  358. X
  359. X        if (nxmt_ela == 0) nxmt_ela = 1;
  360. X        pct = ((nxmt_cpu / nxmt_ela) * 100.0 + 0.5);
  361. X
  362. X        printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nxmt, nxmt_accept, nxmt_refuse, nxmt_failed, they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  363. X
  364. X        printf("Transmission Connection Attempts         ------errors-------\n");
  365. X        printf("System                     Conn    OK    NS   Net   Rmt  Pct\n");
  366. X        for(s in xmt) {
  367. X            tot = conn[s];
  368. X            if (tot == 0) tot = 1;
  369. X            errs = rmt_fail[s] + ns_fail[s] + open_fail[s];
  370. X            ok = (conn[s] - errs);
  371. X            printf("%-25s %5d %5d %5d %5d %5d %3d%%\n", s, conn[s], ok, ns_fail[s], open_fail[s], rmt_fail[s], (100.0 * errs / tot + 0.5));
  372. X            ct_tot += conn[s];
  373. X            ct_ok  += ok;
  374. X            ct_ns  += ns_fail[s];
  375. X            ct_net += open_fail[s];
  376. X            ct_rmt += rmt_fail[s];
  377. X        }
  378. X        tot = ct_tot;
  379. X        if (tot == 0) tot = 1;
  380. X        errs = ct_ns + ct_net + ct_rmt;
  381. X        printf("\n%-25s %5d %5d %5d %5d %5d %3d%%\n\n", "TOTALS", ct_tot, ct_ok, ct_ns, ct_net, ct_rmt, (100.0 * errs / tot + 0.5));
  382. X    }
  383. X
  384. X###############################################################################
  385. X### Article Readership Statistics                                           ###
  386. X###############################################################################
  387. X
  388. X    if (readers) {
  389. X        printf("NNTP readership statistics\n");
  390. X        printf("System                     Conn Articles Groups Post   Elapsed       CPU  Pct\n");
  391. X        for(s in systems) {
  392. X###
  393. X### servers are different animals; they don't belong in this part of the report
  394. X###
  395. X            if (servers[s] > 0 && groups[s] == 0 && articles[s] == 0)
  396. X                continue;
  397. X###
  398. X### report the curious server pokers elsewhere
  399. X###
  400. X            if (groups[s] == 0 && articles[s] == 0 && post[s] == 0) {
  401. X                unknown[s] += systems[s];
  402. X                curious = 1;
  403. X                continue;
  404. X            }
  405. X
  406. X            nconn += systems[s];
  407. X            nart += articles[s];
  408. X            ngrp += groups[s];
  409. X            npost += post[s];
  410. X            ncpu += cpu[s];
  411. X            nela += ela[s];
  412. X
  413. X            e_hours      = ela[s] / 3600;
  414. X            e_sec        = ela[s] % 3600;
  415. X            e_min        = e_sec / 60;
  416. X            e_sec        %= 60;
  417. X
  418. X            c_hours      = cpu[s] / 3600;
  419. X            c_sec        = cpu[s] % 3600;
  420. X            c_min        = c_sec / 60;
  421. X            c_sec        %= 60;
  422. X
  423. X            elapsed = ela[s];
  424. X            if (elapsed == 0) elapsed = 1;
  425. X            pct = ((cpu[s] / elapsed) * 100 + 0.5);
  426. X
  427. X            printf("%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, systems[s], articles[s], groups[s], post[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  428. X        }
  429. X
  430. X        e_hours      = nela / 3600;
  431. X        e_sec        = nela % 3600;
  432. X        e_min        = e_sec / 60;
  433. X        e_sec        %= 60;
  434. X
  435. X        c_hours      = ncpu / 3600;
  436. X        c_sec        = ncpu % 3600;
  437. X        c_min        = c_sec / 60;
  438. X        c_sec        %= 60;
  439. X
  440. X        if (nela == 0) nela = 1;
  441. X        pct = ((ncpu / nela) * 100 + 0.5);
  442. X
  443. X        printf("\n%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nconn, nart, ngrp, npost, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
  444. X    }
  445. X
  446. X###############################################################################
  447. X    if (curious) {
  448. X        printf("Unknown NNTP server explorers\nSystem                     Conn\n");
  449. X        for(s in unknown) {
  450. X            printf("%-25s %5d\n", s, unknown[s]);
  451. X        }
  452. X        printf("\n");
  453. X    }
  454. X###############################################################################
  455. X    if (timeouts) {
  456. X        printf("Server timeouts\n");
  457. X        for(s in timeout) {
  458. X            printf("%-25s %5d\n", s, timeout[s]);
  459. X        }
  460. X        printf("\n");
  461. X    }
  462. X###############################################################################
  463. X    if (readers) {
  464. X        for(g in ng) {
  465. X            x = length(g);
  466. X            if (x > max) max = x;
  467. X
  468. X            i = index(g, ".");
  469. X            if (i > 0) top = substr(g, 1, i - 1);
  470. X            else top = g;
  471. X            category[top] += ng[g];
  472. X        }
  473. X        fmt = sprintf("%%-%ds %%5d\n", max);
  474. X
  475. X        printf("Newsgroup Request Counts (by category)\n");
  476. X        for(g in category) printf(fmt, g, category[g]);
  477. X
  478. X        printf("\nNewsgroup Request Counts (by newsgroup)\n");
  479. X        for(g in ng) printf(fmt, g, ng[g]);
  480. X        printf("\n");
  481. X    }
  482. X}
  483. END_OF_FILE
  484. if test 12577 -ne `wc -c <'./support/nntp_awk.ucbvax'`; then
  485.     echo shar: \"'./support/nntp_awk.ucbvax'\" unpacked with wrong size!
  486. fi
  487. # end of './support/nntp_awk.ucbvax'
  488. fi
  489. if test -f './xmit/nntpxmit.c' -a "${1}" != "-c" ; then 
  490.   echo shar: Will not clobber existing file \"'./xmit/nntpxmit.c'\"
  491. else
  492. echo shar: Extracting \"'./xmit/nntpxmit.c'\" \(26902 characters\)
  493. sed "s/^X//" >'./xmit/nntpxmit.c' <<'END_OF_FILE'
  494. X/* nntpxmit - transmit netnews articles across the internet with nntp
  495. X**
  496. X** This program is for transmitting netnews articles between sites
  497. X** that offer the NNTP service, internet style. There are two forms
  498. X** of article transmission that can be used in this environment, since
  499. X** the communication is interactive (and relatively more immediate,
  500. X** when compared to batched file transfer protocols, like UUCP). They
  501. X** are: active send (I have `x', do you want it?) and polling (what
  502. X** have you gotten lately?).
  503. X**
  504. X**         A C T I V E   S E N D
  505. X**
  506. X** Sites on the UUCP network generally use active send, without asking
  507. X** in advance (that is, unless you got an article from your neighbor,
  508. X** or their site is listed in the Path: header already, you assume
  509. X** they don't have it and send it along). There is an ihave/sendme
  510. X** protocol for doing active send over batched links, but I claim that
  511. X** it won't work well because of the high latency between queueing
  512. X** and actual transfer that UUCP links typically have. That is, you'll
  513. X** still end up with a high rate of duplicate articles being sent over
  514. X** that type of link.
  515. X**
  516. X** With NNTP-based IHAVE, the update window in which another site can
  517. X** give the remote the article you just offered him is the time between
  518. X** the remote telling you it doesn't have the article, and your
  519. X** completed transfer of the article (pretty small). In practice, we
  520. X** still get duplicates, but generally from two problems: synchronized
  521. X** transmission of an article from two different neighbors (this can
  522. X** only be fixed by putting inews(1) into nntpd), and by articles
  523. X** being accepting during an expire(1) run (expire locks out inews
  524. X** processing while it is running, and articles collect until expire
  525. X** is done; since accepted article message-ids aren't added to
  526. X** the history file until expire is done, several clients can offer
  527. X** you the same article, and you'll accept all the copies offered you.
  528. X** When rnews gets run after expire, it will reject the duplicates).
  529. X**
  530. X**         P O L L I N G
  531. X**
  532. X** Polling presents some article and distribution security problems,
  533. X** because the server has no contol over what a transmission client
  534. X** will ask for, and it must therefore control what it tells a client
  535. X** in response to a query.
  536. X**
  537. X** Articles that appear in local newsgroup hierarchies, or appear in
  538. X** the generally distributed USENET newsgroups with local distributions
  539. X** have to be filtered out from the list of message-IDs that the server
  540. X** gives to a client in response to a NEWNEWS query, or filtered when
  541. X** the server fetches the articles off the disk in response to an
  542. X** ARTICLE command (and therefore has complete access to the required
  543. X** information). Otherwise, distributions will leak.
  544. X**
  545. X** The other problem with polling is that a good client should keep track
  546. X** of when it last successfully polled a server, so that it doesn't force
  547. X** h server to dump its entire history file across the network, and this
  548. X** involves more file locking and manipulations routines.
  549. X**
  550. X** nntpxmit only implements active send, for now.
  551. X**
  552. X** Erik E. Fair <fair@ucbarpa.berkeley.edu>, Dec 4, 1987
  553. X*/
  554. X
  555. X#include "nntpxmit.h"
  556. X#include <stdio.h>
  557. X#include <errno.h>
  558. X#include <ctype.h>
  559. X#include <sys/types.h>
  560. X#include <sys/time.h>
  561. X#ifdef    BSD4_2
  562. X#include <sys/resource.h>
  563. X#else
  564. X#include <sys/times.h>
  565. extern    time_t    time();
  566. X#endif    BSD4_2
  567. X#include <sys/file.h>
  568. X#include <fcntl.h>
  569. X#include <signal.h>
  570. X#ifdef USG
  571. X#include "sysexits.h"
  572. X#else
  573. X#include <sysexits.h>
  574. X#endif
  575. X#ifdef    SYSLOG
  576. X#include <syslog.h>
  577. X#endif    SYSLOG
  578. X#include "nntp.h"
  579. X#include "llist.h"
  580. X
  581. X#define    MAXFNAME    BUFSIZ    /* maximum filename size - big enough? */
  582. X#define    FCLOSE(fp)    (void) fclose(fp); (fp) = (FILE *)NULL
  583. X
  584. FILE    *getfp();
  585. char    *errmsg();
  586. void    requeue();
  587. void    catchsig();
  588. void    restsig();
  589. void    logstats();
  590. void    log();
  591. int    interrupted();
  592. X
  593. X/*
  594. X** Globals that certain things need.
  595. X**
  596. X** Various subroutines want the program name to report errors.
  597. X** The queue file, queue file pointer and current article name are
  598. X** there to write out the state of the queue file from a signal handler
  599. X** (that is, the list of unsent and (possibly) failed articles) so
  600. X** that when next we try sending to a given remote site, we don't send
  601. X** stuff we've already sent.
  602. X*/
  603. char    *Pname;            /* this program's invocation name */
  604. char    *Host;            /* current remote host */
  605. char    *Qfile;            /* current queue file we're operating on */
  606. FILE    *Qfp;            /* the (FILE *) for above */
  607. char    Article[MAXFNAME];    /* current article filename */
  608. X
  609. X/*
  610. X** Some flags, toggled by arguments
  611. X*/
  612. X#define    TOGGLE(boolean)    (boolean) = !(boolean)
  613. char    Debug = FALSE;
  614. char    Report_Stats = TRUE;
  615. char    ReQueue_Fails = TRUE;
  616. X
  617. char    *USAGE = "USAGE: nntpxmit [-d][-s][-r][-T][-F][-D] hostname|hostname:file [...]";
  618. char    *Fmt = "%s: %s\n";
  619. char    *E_fopen = "fopen(%s, \"%s\"): %s";
  620. char    *E_unlk = "unlink(%s): %s";
  621. X#ifdef    USELOG
  622. char    *NNTPlog = USELOG;    /* yet another external log file */
  623. FILE    *Logfp = (FILE *)NULL;
  624. X#endif    USELOG
  625. X
  626. ll_t    FailedArticles;        /* list of failed articles */
  627. X
  628. struct {
  629. X    u_long    offered;
  630. X    u_long    accepted;
  631. X    u_long    rejected;
  632. X    u_long    failed;
  633. X} Stats = {0L, 0L, 0L, 0L};
  634. X
  635. double Tbegin, Tend;        /* transfer timestamps */
  636. X
  637. extern    int    errno;
  638. extern     int    strncmp();
  639. extern    char    *rindex();
  640. extern    char    *index();
  641. extern    char    *mktemp();
  642. extern    char    *strcpy();
  643. X
  644. X#ifdef    USG
  645. void
  646. bzero(s, l)
  647. register caddr_t s;
  648. register int    l;
  649. X{
  650. X    while(l-- > 0) *s++ = 0;
  651. X}
  652. X#endif    USG
  653. X
  654. main(ac, av)
  655. int    ac;
  656. char    *av[];
  657. X{
  658. X    register int    i;
  659. X    int    transport = T_IP_TCP;    /* default is IP/TCP */
  660. X    int    isQfile = TRUE;        /* file arg is a Queue file */
  661. X#ifdef    USELOG
  662. X    char    *amode = "a";
  663. X#endif    USELOG
  664. X#ifdef    BSD4_2
  665. X    struct timeval tod;
  666. X    struct timezone tz;
  667. X
  668. X    (void) gettimeofday(&tod, &tz);
  669. X    Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
  670. X#else
  671. X    Tbegin = (double) time((time_t *)NULL);
  672. X#endif    BSD4_2
  673. X
  674. X    Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
  675. X    
  676. X    if (ac < 2) {
  677. X        fprintf(stderr, Fmt, Pname, USAGE);
  678. X        exit(EX_USAGE);
  679. X    }
  680. X
  681. X#ifdef    SYSLOG
  682. X    /* 4.2 BSD openlog has only two args */
  683. X#ifdef    LOG_LOCAL7
  684. X    (void) openlog(Pname, LOG_PID, LOG_LOCAL7);
  685. X#else
  686. X    (void) openlog(Pname, LOG_PID);
  687. X#endif    LOG_LOCAL_7
  688. X#endif    SYSLOG
  689. X#ifdef    USELOG
  690. X    if ((Logfp = fopen(NNTPlog, amode)) == (FILE *)NULL) {
  691. X        char    buf[BUFSIZ];
  692. X
  693. X        sprintf(buf, E_fopen, NNTPlog, amode, errmsg(errno));
  694. X        log(L_NOTICE, buf);
  695. X    }
  696. X#endif    USELOG
  697. X
  698. X    for(i = 1; i < ac; i++) {
  699. X        if (av[i][0] == '-') {
  700. X            switch(av[i][1]) {
  701. X            case 'T':
  702. X                transport = T_IP_TCP;
  703. X                break;
  704. X            case 'D':
  705. X                transport = T_DECNET;
  706. X                break;
  707. X            case 'F':
  708. X                transport = T_FD;
  709. X                break;
  710. X            case 's':
  711. X                TOGGLE(Report_Stats);
  712. X                break;
  713. X            case 'd':
  714. X                TOGGLE(Debug);
  715. X                break;
  716. X            case 'r':
  717. X                TOGGLE(ReQueue_Fails);
  718. X                break;
  719. X            case 'a':
  720. X                isQfile = FALSE;
  721. X                break;
  722. X            default:
  723. X                fprintf(stderr, "%s: no such option: -%c\n",
  724. X                    Pname, av[i][1]);
  725. X                fprintf(stderr, Fmt, Pname, USAGE);
  726. X                exit(EX_USAGE);
  727. X            }
  728. X            continue;
  729. X        }
  730. X
  731. X        /*
  732. X        ** OK, it wasn't an option, therefore it must be a
  733. X        ** hostname, filename pair.
  734. X        **
  735. X        ** If the user typed host::file, then it's DECNET,
  736. X        ** whether they remembered the "-D" option or not.
  737. X        */
  738. X        Host = av[i];
  739. X        if ((Qfile = index(Host, ':')) != (char *)NULL) {
  740. X            if (Qfile[1] == ':') {
  741. X                transport = T_DECNET;
  742. X                *Qfile++ = '\0';
  743. X            } else if (transport != T_FD)
  744. X                transport = T_IP_TCP;
  745. X            *Qfile++ = '\0';
  746. X        } else
  747. X            Qfile = Host;
  748. X
  749. X        bzero((caddr_t)&Stats, sizeof(Stats));
  750. X        if (isQfile) {
  751. X            if (sendnews(Host, transport, Qfile, isQfile) && Report_Stats) {
  752. X                logstats();
  753. X            }
  754. X        } else {
  755. X            /* one-shot */
  756. X            (void) strcpy(Article, Qfile);
  757. X            exit(sendnews(Host, transport, Qfile, isQfile) ? EX_OK : EX_TEMPFAIL);
  758. X        }
  759. X    }
  760. X    exit(EX_OK);
  761. X}
  762. X
  763. X/*
  764. X** Calculate how much time we've used,
  765. X** and report that (and the transfer statistics).
  766. X**
  767. X*/
  768. void
  769. logstats()
  770. X{
  771. X    static double ouser = 0.0, osys = 0.0;
  772. X    double user, sys;
  773. X    char buf[BUFSIZ];
  774. X#ifdef    USELOG
  775. X#ifdef    BSD4_2
  776. X    extern    time_t    time();
  777. X#endif    BSD4_2
  778. X    time_t    tstamp;
  779. X    char    *tp;
  780. X    extern    char    *ctime();
  781. X#endif    USELOG
  782. X#ifdef    BSD4_2
  783. X    struct rusage self, kids;
  784. X    struct timeval tod;
  785. X    struct timezone tzdummy;
  786. X
  787. X    (void) getrusage(RUSAGE_SELF, &self);
  788. X    (void) getrusage(RUSAGE_CHILDREN, &kids);
  789. X    (void) gettimeofday(&tod, &tzdummy);
  790. X
  791. X    Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;
  792. X
  793. X    user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
  794. X        (double) self.ru_utime.tv_usec/1000000. +
  795. X        (double) kids.ru_utime.tv_usec/1000000.;
  796. X    
  797. X    sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
  798. X        (double) self.ru_stime.tv_usec/1000000. +
  799. X        (double) kids.ru_stime.tv_usec/1000000.;
  800. X#else
  801. X#define    HZ    60.0    /* typical system clock ticks - param.h */
  802. X    struct tms    cpu;
  803. X
  804. X    (void) times(&cpu);
  805. X
  806. X    Tend = (double) time((time_t *)NULL);
  807. X    user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
  808. X    sys  = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
  809. X#endif    BSD4_2
  810. X    sprintf(buf,
  811. X        "%s stats %lu offered %lu accepted %lu rejected %lu failed",
  812. X        Host, Stats.offered, Stats.accepted, Stats.rejected,
  813. X        Stats.failed);
  814. X    log(L_INFO, buf);
  815. X#ifdef     USELOG
  816. X    if (Logfp != (FILE *)NULL) {
  817. X        (void) time(&tstamp);
  818. X        tp = ctime(&tstamp);
  819. X        tp[19] = '\0';
  820. X        fprintf(Logfp, Fmt, &tp[4], buf);
  821. X    }
  822. X#endif    USELOG
  823. X
  824. X    sprintf(buf, "%s xmit user %.1f system %.1f elapsed %.1f",
  825. X        Host, (user - ouser), (sys - osys), (Tend - Tbegin));
  826. X    log(L_INFO, buf);
  827. X#ifdef     USELOG
  828. X    if (Logfp != (FILE *)NULL) {
  829. X        fprintf(Logfp, Fmt, &tp[4], buf);
  830. X        (void) fflush(Logfp);
  831. X    }
  832. X#endif    USELOG
  833. X
  834. X    /* reset reference point */
  835. X    Tbegin = Tend;    
  836. X    ouser = user;
  837. X    osys = sys;
  838. X}
  839. X
  840. X/*
  841. X** Given a hostname to connect to, and a file of filenames (which contain
  842. X** netnews articles), send those articles to the named host using NNTP.
  843. X**
  844. X** Return code behavior is different depending upon isQfile.
  845. X**
  846. X**    TRUE    - return TRUE if we contacted the remote and started
  847. X**          transferring news - this is to decide whether to
  848. X**          record CPU and transfer statistics.
  849. X**
  850. X**    FALSE    - a one-shot file transfer - return TRUE or FALSE depending
  851. X**          upon whether we successfully transferred the one article.
  852. X*/
  853. sendnews(host, transport, file, isQfile)
  854. char    *host, *file;
  855. int    transport, isQfile;
  856. X{
  857. X    register FILE    *fp;
  858. X#ifdef    FTRUNCATE
  859. X    char    *mode = "r+";        /* so we can use ftruncate() */
  860. X#else
  861. X    char    *mode = "r";
  862. X#endif    FTRUNCATE
  863. X
  864. X    if ((Qfp = fopen(file, mode)) == (FILE *)NULL) {
  865. X        char    buf[BUFSIZ];
  866. X
  867. X        sprintf(buf, E_fopen, file, mode, errmsg(errno));
  868. X        log(L_WARNING, buf);
  869. X        return(FALSE);
  870. X    }
  871. X
  872. X    /*
  873. X    ** interlock with other copies of this process.
  874. X    ** non-blocking.
  875. X    */
  876. X    if (isQfile) {
  877. X        if (!lockfd(fileno(Qfp), file, DONT_BLOCK)) {
  878. X            FCLOSE(Qfp);
  879. X            return(FALSE);
  880. X        }
  881. X    }
  882. X
  883. X    /*
  884. X    ** Open a connection to the remote server
  885. X    */
  886. X    if (hello(host, transport) == FAIL) {
  887. X        FCLOSE(Qfp);
  888. X        return(FALSE);
  889. X    }
  890. X
  891. X    if (isQfile) {
  892. X        /*
  893. X        ** We're sending a batch queue:
  894. X        **    open article
  895. X        **    get message-ID
  896. X        **    send "IHAVE <message-ID>" to remote
  897. X        **    read their reply
  898. X        **    send article if appropriate
  899. X        **    iterate to end of queue file
  900. X        */
  901. X        catchsig(interrupted);
  902. X
  903. X        while((fp = getfp(Qfp, Article, sizeof(Article))) != (FILE *)NULL) {
  904. X            if (!sendarticle(host, fp)) {
  905. X                (void) fclose(fp);
  906. X                requeue(Article);
  907. X                Article[0] = '\0';
  908. X                cleanup();
  909. X                goodbye(DONT_WAIT);
  910. X                restsig();
  911. X                return(TRUE);
  912. X            }
  913. X            (void) fclose(fp);
  914. X        }
  915. X
  916. X        cleanup();
  917. X        goodbye(WAIT);
  918. X        restsig();
  919. X        return(TRUE);
  920. X    } else {
  921. X        /*
  922. X        ** Qfp is a netnews article - this is a one-shot
  923. X        ** operation, exit code dependent upon remote's
  924. X        ** acceptance of the article
  925. X        */
  926. X        register int    retcode;
  927. X
  928. X        retcode = sendarticle(host, Qfp);
  929. X        FCLOSE(Qfp);
  930. X        goodbye(retcode ? WAIT : DONT_WAIT);
  931. X        return(retcode && Stats.accepted == 1 && Stats.failed == 0);
  932. X    }
  933. X}
  934. X
  935. X/*
  936. X** Perform one transfer operation:
  937. X**    Give IHAVE command
  938. X**    Wait for reply, and send article if they ask for it
  939. X**    Wait for transfer confirmation, and requeue the article
  940. X**        if they drop it.
  941. X**    Watch all network I/O for errors, return FALSE if
  942. X**        the connection fails and we have to cleanup.
  943. X*/
  944. sendarticle(host, fp)
  945. char    *host;
  946. FILE    *fp;
  947. X{
  948. X    register int    code;
  949. X    char    buf[BUFSIZ];
  950. X    char    *e_xfer = "%s xfer: %s";
  951. X
  952. X    switch(code = ihave(fp)) {
  953. X    case CONT_XFER:
  954. X        /*
  955. X        ** They want it. Give it to 'em.
  956. X        */
  957. X        if (!sendfile(fp)) {
  958. X            sprintf(buf, e_xfer, host, errmsg(errno));
  959. X            log(L_NOTICE, buf);
  960. X            Stats.failed++;
  961. X            return(FALSE);
  962. X        }
  963. X        /*
  964. X        ** Did the article transfer OK?
  965. X        ** Stay tuned to this same socket to find out!
  966. X        */
  967. X        if ((code = readreply(buf, sizeof(buf))) != OK_XFERED) {
  968. X            Stats.failed++;
  969. X            if (code < 0) {
  970. X                if (errno > 0) {
  971. X                    sprintf(buf, e_xfer, host, errmsg(errno));
  972. X                    log(L_NOTICE, buf);
  973. X                } else {
  974. X                    char errbuf[BUFSIZ];
  975. X
  976. X                    sprintf(errbuf, e_xfer, host, buf);
  977. X                    log(L_NOTICE, errbuf);
  978. X                }
  979. X                return(FALSE);
  980. X            }
  981. X            if (ReQueue_Fails && code != ERR_XFERRJCT) {
  982. X                requeue(Article);
  983. X                Article[0] = '\0';
  984. X            }
  985. X        }
  986. X        break;
  987. X    case ERR_GOTIT:
  988. X        /* they don't want it */
  989. X        break;
  990. X    default:
  991. X        if (code < 0) {
  992. X            if (errno > 0) {
  993. X                sprintf(buf, e_xfer, host, errmsg(errno));
  994. X                log(L_NOTICE, buf);
  995. X            } else {
  996. X                sprintf(buf, e_xfer, host, "ihave");
  997. X                log(L_NOTICE, buf);
  998. X            }
  999. X        } else {
  1000. X            sprintf(buf, "%s improper response to IHAVE: %d while offering %s", host, code, Article);
  1001. X            log(L_WARNING, buf);
  1002. X        }
  1003. X        return(FALSE);
  1004. X    }
  1005. X    return(TRUE);
  1006. X}
  1007. X
  1008. char *
  1009. errmsg(code)
  1010. int code;
  1011. X{
  1012. X    extern int sys_nerr;
  1013. X    extern char *sys_errlist[];
  1014. X    static char ebuf[6+5+1];
  1015. X
  1016. X    if (code > sys_nerr || code < 0) {
  1017. X        (void) sprintf(ebuf, "Error %d", code);
  1018. X        return ebuf;
  1019. X    } else
  1020. X        return sys_errlist[code];
  1021. X}
  1022. X
  1023. X/*
  1024. X** strip leading and trailing spaces
  1025. X*/
  1026. char *
  1027. sp_strip(s)
  1028. register char    *s;
  1029. X{
  1030. X    register char    *cp;
  1031. X
  1032. X    if (s == NULL)
  1033. X        return(NULL);
  1034. X
  1035. X    if (*s == '\0')
  1036. X        return(s);
  1037. X    
  1038. X    cp = &s[strlen(s) - 1];
  1039. X    while(cp > s && isspace(*cp))
  1040. X        cp--;
  1041. X
  1042. X    *++cp = '\0';    /* zap trailing spaces */
  1043. X
  1044. X    for(cp = s; *cp && isspace(*cp); cp++)
  1045. X        continue;
  1046. X
  1047. X    return(cp);    /* return pointer to first non-space */
  1048. X}
  1049. X
  1050. X/*
  1051. X** convert `s' to lower case
  1052. X*/
  1053. char *
  1054. lcase(s)
  1055. register char    *s;
  1056. X{
  1057. X    register char    *cp;
  1058. X
  1059. X    if (s == (char *)NULL)
  1060. X        return(s);
  1061. X
  1062. X    for(cp = s; *cp != '\0'; cp++)
  1063. X        if (isupper(*cp))
  1064. X            *cp = tolower(*cp);
  1065. X    return(s);
  1066. X}
  1067. X
  1068. X/*
  1069. X** Get the message-id header field data with a minimum of fuss.
  1070. X*/
  1071. char *
  1072. getmsgid(fp)
  1073. FILE *fp;
  1074. X{
  1075. X    static    char    buf[BUFSIZ];
  1076. X    static    char    *msgid = "message-id";
  1077. X    register char    *cp, *cp2;
  1078. X
  1079. X    while(fgets(buf, sizeof(buf), fp) != (char *)NULL) {
  1080. X        switch(buf[0]) {
  1081. X        case '\n':
  1082. X            return((char *)NULL);    /* EOH, we failed */
  1083. X        case 'M':
  1084. X        case 'm':
  1085. X            if ((cp = index(buf, ':')) == (char *)NULL)
  1086. X                continue;
  1087. X            *cp++ = '\0';
  1088. X            if (strncmp(lcase(buf), msgid, sizeof(*msgid)) == 0) {
  1089. X                /* dump extraneous trash - umass.bitnet */
  1090. X                /* hope nobody quotes an '>' in a msgid */
  1091. X                if ((cp2 = index(cp, '>')) != (char *)NULL)
  1092. X                    *++cp2 = '\0';
  1093. X                return(sp_strip(cp));
  1094. X            }
  1095. X            break;
  1096. X        }
  1097. X    }
  1098. X    return((char *)NULL);    /* EOF, we failed */
  1099. X}
  1100. X
  1101. X#ifdef    notdef    /* nobody obeys the triply damned protocol anyway! */
  1102. X/*
  1103. X** Special characters, see RFC822, appendix D.
  1104. X*/
  1105. isspecial(c)
  1106. char    c;
  1107. X{
  1108. X    char    *specials = "()<>@,;:\\\".[]";
  1109. X
  1110. X    return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
  1111. X}
  1112. X
  1113. X/*
  1114. X** Check on the validity of an RFC822 message-id
  1115. X**
  1116. X** By The Book, RFC822 Appendix D.
  1117. X**    msg-id        = "<" addr-spec ">"
  1118. X**    addr-spec    = local-part "@" domain
  1119. X**    local-part    = word *("." word)
  1120. X**    word        = atom / quoted-string
  1121. X**    domain         = sub-domain *("." sub-domain)
  1122. X**    sub-domain    = domain-ref / domain-literal
  1123. X**    domain-ref    = atom
  1124. X**    domain-literal    = "[" *(dtext / quoted-pair) "]"
  1125. X**
  1126. X** NOTE: close reading of the RFC822 spec indicates that a fully
  1127. X**    qualified domain name (i.e. one with at least one dot) is
  1128. X**    NOT required in the domain part of the addr-spec. However,
  1129. X**    I've decided to be an asshole and require them, since we'll 
  1130. X**    all die a slow death later on if I don't at this juncture.
  1131. X**    To disable, if you disagree with me, see the last return
  1132. X**    statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu>
  1133. X**    May 30, 1986
  1134. X*/
  1135. msgid_ok(id)
  1136. register char    *id;
  1137. X{
  1138. X    register Langle = FALSE;
  1139. X    register Rangle = FALSE;
  1140. X    register local_part = FALSE;
  1141. X    register at = FALSE;
  1142. X    register dot = FALSE;
  1143. X
  1144. X    /* skip up to the opening angle bracket */
  1145. X    if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
  1146. X        return(FALSE);        /* don't waste my time! */
  1147. X
  1148. X    for(; *id != '\0'; id++) {
  1149. X        switch(*id) {
  1150. X        case '<':
  1151. X            if (Langle) return(FALSE);
  1152. X            Langle = local_part = TRUE;
  1153. X            break;
  1154. X        case '>':
  1155. X            if (Rangle || !Langle || !at) return(FALSE);
  1156. X            else Rangle = TRUE;
  1157. X            break;
  1158. X        case '@':        /* should be a domain spec */
  1159. X            at = TRUE;
  1160. X            local_part = FALSE;
  1161. X            break;
  1162. X        case '.':
  1163. X            dot = at;
  1164. X            break;
  1165. X        case '\\':
  1166. X            /*
  1167. X            ** quoted pair; this disallows NULs, but how
  1168. X            ** many mailers would die if someone used one?
  1169. X            */
  1170. X            if (!local_part || (*++id) == '\0') return(FALSE);
  1171. X            break;
  1172. X        case '"':
  1173. X            /*
  1174. X            ** quoted string
  1175. X            */
  1176. X            if (!local_part) return(FALSE);
  1177. X            do {
  1178. X                switch(*++id) {
  1179. X                case '\\':
  1180. X                    if ((*++id) == '\0') return(FALSE);
  1181. X                    break;
  1182. X                case '\r':
  1183. X                    return(FALSE);
  1184. X                }
  1185. X            } while(*id != '\0' && *id != '"');
  1186. X            break;
  1187. X        case '[':
  1188. X            /*
  1189. X            ** domain literal
  1190. X            */
  1191. X            if (local_part) return(FALSE);
  1192. X            do {
  1193. X                switch(*++id) {
  1194. X                case '\\':
  1195. X                    if ((*++id) == '\0') return(FALSE);
  1196. X                    break;
  1197. X                case '\r':
  1198. X                    return(FALSE);
  1199. X                }
  1200. X            } while(*id != '\0' && *id != ']');
  1201. X            break;
  1202. X        default:
  1203. X            if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
  1204. X                return(FALSE);    /* quit immediately */
  1205. X            break;
  1206. X        }
  1207. X    }
  1208. X    return(at && dot && Langle && Rangle);
  1209. X}
  1210. X#else notdef
  1211. X
  1212. X/*
  1213. X** Simpleton's check for message ID syntax.
  1214. X** A concession to the realities of the ARPA Internet.
  1215. X*/
  1216. msgid_ok(s)
  1217. register char *s;
  1218. X{
  1219. X    register char    c;
  1220. X    register in_msgid = FALSE;
  1221. X
  1222. X    if (s == (char *)NULL)
  1223. X        return(FALSE);
  1224. X
  1225. X    while((c = *s++) != '\0') {
  1226. X        if (!isascii(c) || iscntrl(c) || isspace(c))
  1227. X            return(FALSE);
  1228. X        switch(c) {
  1229. X        case '<':
  1230. X            in_msgid = TRUE;
  1231. X            break;
  1232. X        case '>':
  1233. X            return(in_msgid);
  1234. X        }
  1235. X    }
  1236. X    return(FALSE);
  1237. X}
  1238. X#endif    notdef
  1239. X
  1240. X/*
  1241. X** Read the header of a netnews article, snatch the message-id therefrom,
  1242. X** and ask the remote if they have that one already.
  1243. X*/
  1244. ihave(fp)
  1245. FILE    *fp;
  1246. X{
  1247. X    register int    code;
  1248. X    register char    *id;
  1249. X    char    buf[BUFSIZ];
  1250. X
  1251. X    if ((id = getmsgid(fp)) == (char *)NULL || *id == '\0') {
  1252. X        /*
  1253. X        ** something botched locally with the article
  1254. X        ** so we don't send it, but we don't break off
  1255. X        ** communications with the remote either.
  1256. X        */
  1257. X        sprintf(buf, "%s: message-id missing!", Article);
  1258. X        log(L_DEBUG, buf);
  1259. X        return(ERR_GOTIT);
  1260. X    }
  1261. X
  1262. X    if (!msgid_ok(id)) {
  1263. X        sprintf(buf, "%s: message-id syntax error: %s", Article, id);
  1264. X        log(L_DEBUG, buf);
  1265. X        return(ERR_GOTIT);
  1266. X    }
  1267. X
  1268. X    sprintf(buf, "IHAVE %s", id);
  1269. X    Stats.offered++;
  1270. X
  1271. X    switch(code = converse(buf, sizeof(buf))) {
  1272. X    case CONT_XFER:
  1273. X        Stats.accepted++;
  1274. X        rewind(fp);
  1275. X        return(code);
  1276. X    case ERR_GOTIT:
  1277. X        Stats.rejected++;
  1278. X        return(code);
  1279. X    default:
  1280. X        return(code);
  1281. X    }
  1282. X}
  1283. X
  1284. X/*
  1285. X** Given that fp points to an open file containing filenames,
  1286. X** open and return a file pointer to the next filename in the file.
  1287. X** Don't you love indirection?
  1288. X**
  1289. X** Returns a valid FILE pointer or NULL if end of file.
  1290. X*/
  1291. FILE *
  1292. getfp(fp, filename, fnlen)
  1293. register FILE    *fp;
  1294. char    *filename;
  1295. register int    fnlen;
  1296. X{
  1297. X    register FILE    *newfp = (FILE *)NULL;
  1298. X    register char    *cp;
  1299. X    char    *mode = "r";
  1300. X
  1301. X    while(newfp == (FILE *)NULL) {
  1302. X        if (fgets(filename, fnlen, fp) == (char *)NULL)
  1303. X            return((FILE *)NULL);        /* EOF, tell caller */
  1304. X
  1305. X        filename[fnlen - 1] = '\0';    /* make sure */
  1306. X
  1307. X        /* if fgets() ever forgets the '\n', we're fucked */
  1308. X        if (*(cp = &filename[strlen(filename) - 1]) == '\n')
  1309. X            *cp = '\0';
  1310. X
  1311. X        if (filename[0] == '\0')
  1312. X            continue;
  1313. X
  1314. X        if ((newfp = fopen(filename, mode)) == (FILE *)NULL) {
  1315. X            /*
  1316. X            ** The only permissible error is `file non-existant'
  1317. X            ** anything else indicates something is seriously
  1318. X            ** wrong, and we should go away to let the shell
  1319. X            ** script clean up.
  1320. X            */
  1321. X            if (errno != ENOENT) {
  1322. X                char    buf[BUFSIZ];
  1323. X
  1324. X                sprintf(buf, E_fopen, filename, mode, errmsg(errno));
  1325. X                log(L_WARNING, buf);
  1326. X                goodbye(DONT_WAIT);
  1327. X                exit(EX_OSERR);
  1328. X            }
  1329. X        }
  1330. X    }
  1331. X    return(newfp);
  1332. X}
  1333. X
  1334. X/*
  1335. X** OK, clean up any mess and requeue failed articles
  1336. X*/
  1337. cleanup()
  1338. X{
  1339. X    dprintf(stderr, "%s: cleanup()\n", Pname);
  1340. X    if (Qfp == (FILE *)NULL || Qfile == (char *)NULL)
  1341. X        return;
  1342. X
  1343. X    if ((ReQueue_Fails && Stats.failed > 0) || !feof(Qfp)) {
  1344. X        rewrite();
  1345. X    } else {
  1346. X        /*
  1347. X        ** Nothing to clean up after, reset stuff and
  1348. X        ** nuke the queue file.
  1349. X        */
  1350. X        requeue((char *)NULL);
  1351. X        if (feof(Qfp)) {
  1352. X            dprintf(stderr, "%s: unlink(%s)\n", Pname, Qfile);
  1353. X            if (unlink(Qfile) < 0) {
  1354. X                char    buf[BUFSIZ];
  1355. X
  1356. X                sprintf(buf, E_unlk, Qfile, errmsg(errno));
  1357. X                log(L_WARNING, buf);
  1358. X            }
  1359. X        }
  1360. X        FCLOSE(Qfp);
  1361. X    }
  1362. X}
  1363. X/*
  1364. X** Add an article file name to an allocated linked list,
  1365. X** so that we can rewrite it back to the queue file later.
  1366. X** Calling this with a NULL pointer resets the internal pointer.
  1367. X*/
  1368. void
  1369. requeue(article)
  1370. char *article;
  1371. X{
  1372. X    static ll_t *lp = &FailedArticles;
  1373. X
  1374. X    if (article == (char *)NULL) {
  1375. X        dprintf(stderr, "%s: requeue(): reset\n", Pname);
  1376. X        goto reset;        /* this is for our static pointer */
  1377. X    }
  1378. X
  1379. X    if (*article == '\0')
  1380. X        return;
  1381. X
  1382. X    dprintf(stderr, "%s: requeue(%s)\n", Pname, article);
  1383. X    if ((lp = l_alloc(lp, article, strlen(article) + 1)) == (ll_t *)NULL) {
  1384. X        fprintf(stderr, "%s: requeue(%s) failed, dumping fail list\n",
  1385. X            Pname, article);
  1386. X        /*
  1387. X        ** Wow! Did you know that this could blow the stack
  1388. X        ** if we recurse too deeply? I sure didn't!
  1389. X        */
  1390. reset:
  1391. X        l_free(&FailedArticles);
  1392. X        lp = &FailedArticles;
  1393. X    }
  1394. X}
  1395. X
  1396. X/*
  1397. X** Note that if I'm not running as "news" or "usenet" (or whatever
  1398. X** account is supposed to own netnews), the resultant file will be the
  1399. X** wrong ownership, permissions, etc.
  1400. X*/
  1401. rewrite()
  1402. X{
  1403. X    register ll_t    *lp;
  1404. X    register FILE    *tmpfp;
  1405. X    register int    nart = 0;
  1406. X    char    *mode = "w+";
  1407. X    char    *template = "/tmp/nntpxmitXXXXXX";
  1408. X    char    buf[BUFSIZ];
  1409. X    static char    *tempfile = (char *)NULL;
  1410. X
  1411. X    dprintf(stderr, "%s: rewrite(%s)\n", Pname, Qfile);
  1412. X
  1413. X    if (tempfile == (char *)NULL)        /* should only need this once */
  1414. X        tempfile = mktemp(template);
  1415. X
  1416. X    if ((tmpfp = fopen(tempfile, mode)) == (FILE *)NULL) {
  1417. X        sprintf(buf, E_fopen, tempfile, mode, errmsg(errno));
  1418. X        log(L_WARNING, buf);
  1419. X        FCLOSE(Qfp);
  1420. X        return;
  1421. X    }
  1422. X
  1423. X    /*
  1424. X    ** Requeue the rest of the queue file first,
  1425. X    ** so that failed articles (if any) go to the end
  1426. X    ** of the new file.
  1427. X    */
  1428. X    if (!feof(Qfp)) {
  1429. X        dprintf(stderr, "%s: copying the unused portion of %s to %s\n",
  1430. X            Pname, Qfile, tempfile);
  1431. X        while(fgets(buf, sizeof(buf), Qfp) != (char *)NULL)
  1432. X            (void) fputs(buf, tmpfp);
  1433. X    }
  1434. X
  1435. X    /*
  1436. X    ** Here we write out the filenames of articles which
  1437. X    ** failed at the remote end.
  1438. X    */
  1439. X    dprintf(stderr, "%s: writing failed article filenames to %s\n",
  1440. X        Pname, tempfile);
  1441. X    L_LOOP(lp, FailedArticles) {
  1442. X        fprintf(tmpfp, "%s\n", lp->l_item);
  1443. X        nart++;
  1444. X    }
  1445. X    dprintf(stderr, "%s: wrote %d article filenames to %s\n",
  1446. X        Pname, nart, tempfile);
  1447. X
  1448. X    (void) fflush(tmpfp);
  1449. X    /*
  1450. X    ** If writing the temp file failed (maybe /tmp is full?)
  1451. X    ** back out and leave the queue file exactly as it is.
  1452. X    */
  1453. X    if (ferror(tmpfp)) {
  1454. X        sprintf(buf, "rewrite(): copy to %s failed", tempfile);
  1455. X        log(L_WARNING, buf);
  1456. X        (void) fclose(tmpfp);
  1457. X        FCLOSE(Qfp);
  1458. X        if (unlink(tempfile) < 0) {
  1459. X            sprintf(buf, E_unlk, tempfile, errmsg(errno));
  1460. X            log(L_WARNING, buf);
  1461. X        }
  1462. X        requeue((char *)NULL);        /* reset */
  1463. X        return;
  1464. X    }
  1465. X
  1466. X    rewind(tmpfp);
  1467. X#ifdef    FTRUNCATE
  1468. X    rewind(Qfp);
  1469. X    if (ftruncate(fileno(Qfp), (off_t)0) < 0) {
  1470. X        sprintf(buf, "ftruncate(%s, 0): %s", Qfile, errmsg(errno));
  1471. X        log(L_WARNING, buf);
  1472. X        FCLOSE(Qfp);
  1473. X        (void) fclose(tmpfp);
  1474. X        if (unlink(tempfile) < 0) {
  1475. X            sprintf(buf, E_unlk, tempfile, errmsg(errno));
  1476. X            log(L_WARNING, buf);
  1477. X        }
  1478. X        requeue((char *)NULL);        /* reset */
  1479. X        return;
  1480. X    }
  1481. X#else
  1482. X    FCLOSE(Qfp);    /* we just nuked our lock here (lockfd) */
  1483. X    if ((Qfp = fopen(Qfile, mode)) == (FILE *)NULL) {
  1484. X        sprintf(buf, E_fopen, Qfile, mode, errmsg(errno));
  1485. X        log(L_WARNING, buf);
  1486. X        (void) fclose(tmpfp);
  1487. X        if (unlink(tempfile) < 0) {
  1488. X            sprintf(buf, E_unlk, tempfile, errmsg(errno));
  1489. X            log(L_WARNING, buf);
  1490. X        }
  1491. X        requeue((char *)NULL);        /* reset */
  1492. X        return;
  1493. X    }
  1494. X    /* Try to get our lock back (but continue whether we do or not) */
  1495. X    (void) lockfd(fileno(Qfp), Qfile, DONT_BLOCK);
  1496. X#endif    FTRUNCATE
  1497. X
  1498. X    dprintf(stderr, "%s: copying %s back to %s\n", Pname, tempfile, Qfile);
  1499. X    while(fgets(buf, sizeof(buf), tmpfp) != (char *)NULL)
  1500. X        (void) fputs(buf, Qfp);
  1501. X
  1502. X    (void) fflush(Qfp);
  1503. X    if (ferror(Qfp)) {
  1504. X        sprintf(buf, "rewrite(): copy to %s failed", Qfile);
  1505. X        log(L_WARNING, buf);
  1506. X    }
  1507. X    (void) fclose(tmpfp);
  1508. X    FCLOSE(Qfp);
  1509. X    if (unlink(tempfile) < 0) {
  1510. X        sprintf(buf, E_unlk, tempfile, errmsg(errno));
  1511. X        log(L_WARNING, buf);
  1512. X    }
  1513. X    requeue((char *)NULL);        /* reset */
  1514. X    dprintf(stderr, "%s: rewrite(%s): done\n", Pname, Qfile);
  1515. X    return;
  1516. X}
  1517. X
  1518. X/*
  1519. X** Signal stuff
  1520. X**
  1521. X** There's probably too much stuff to do in this signal
  1522. X** handler, but we're going to exit anyway...
  1523. X*/
  1524. interrupted(sig)
  1525. int    sig;
  1526. X{
  1527. X    char buf[BUFSIZ];
  1528. X
  1529. X#ifndef RELSIG
  1530. X    catchsig(SIG_IGN);    /* for System V - hope we're quick enough */
  1531. X#endif    RELSIG
  1532. X    sprintf(buf, "%s signal %d", Host, sig);
  1533. X    log(L_NOTICE, buf);
  1534. X    requeue(Article);
  1535. X    cleanup();
  1536. X    if (Report_Stats)
  1537. X        logstats();
  1538. X    goodbye(DONT_WAIT);
  1539. X    exit(EX_TEMPFAIL);
  1540. X}
  1541. X
  1542. struct {
  1543. X    int    signo;
  1544. X    ifunp    state;
  1545. X} SigList[] = {
  1546. X    {SIGHUP},
  1547. X    {SIGINT},
  1548. X    {SIGQUIT},
  1549. X    {SIGTERM},
  1550. X    {NULL}
  1551. X};
  1552. X
  1553. void
  1554. catchsig(handler)
  1555. ifunp    handler;
  1556. X{
  1557. X    register int    i;
  1558. X
  1559. X    if (handler != SIG_IGN) {
  1560. X        for(i = 0; SigList[i].signo != NULL; i++) {
  1561. X            SigList[i].state = signal(SigList[i].signo, handler);
  1562. X        }
  1563. X    } else {
  1564. X        for(i = 0; SigList[i].signo != NULL; i++) {
  1565. X            (void) signal(SigList[i].signo, handler);
  1566. X        }
  1567. X    }
  1568. X}
  1569. X
  1570. void
  1571. restsig()
  1572. X{
  1573. X    register int    i;
  1574. X
  1575. X    for(i = 0; SigList[i].signo != NULL; i++) {
  1576. X        if (SigList[i].state != (ifunp)(-1))
  1577. X            (void) signal(SigList[i].signo, SigList[i].state);
  1578. X    }
  1579. X}
  1580. X
  1581. X/*
  1582. X** log stuff
  1583. X*/
  1584. void
  1585. log(importance, error)
  1586. int    importance;
  1587. char    *error;
  1588. X{
  1589. X    FILE    *report = (importance == L_INFO ? stdout : stderr);
  1590. X
  1591. X    fprintf(report, Fmt, Pname, error);
  1592. X#ifdef    SYSLOG
  1593. X    switch(importance) {
  1594. X    case L_INFO:    importance = LOG_INFO;        break;
  1595. X    case L_DEBUG:    importance = LOG_DEBUG;        break;
  1596. X    case L_NOTICE:    importance = LOG_NOTICE;    break;
  1597. X    case L_WARNING:    importance = LOG_WARNING;    break;
  1598. X    default:    importance = LOG_DEBUG;        break;
  1599. X    }
  1600. X    syslog(importance, error);
  1601. X#endif    SYSLOG
  1602. X}
  1603. X
  1604. X/*
  1605. X** Lock a file descriptor
  1606. X**
  1607. X** NOTE: if the appropriate system calls are unavailable,
  1608. X** this subroutine is a no-op.
  1609. X*/
  1610. lockfd(fd, file, non_blocking)
  1611. int    fd, non_blocking;
  1612. char    *file;            /* just for error reporting */
  1613. X{
  1614. X    char    buf[BUFSIZ];
  1615. X#ifdef    USG
  1616. X#ifdef    F_TLOCK
  1617. X    if (lockf(fd, (non_blocking ? F_TLOCK : F_LOCK), 0) < 0) {
  1618. X        if (errno != EACCES) {
  1619. X            sprintf(buf, "lockf(%s): %s\n", file, errmsg(errno));
  1620. X            log(L_WARNING, buf);
  1621. X        }
  1622. X        return(FALSE);
  1623. X    }
  1624. X#endif    F_TLOCK
  1625. X#else
  1626. X#ifdef    LOCK_EX
  1627. X    if (flock(fd, LOCK_EX|(non_blocking ? LOCK_NB : 0)) < 0) {
  1628. X        if (errno != EWOULDBLOCK) {
  1629. X            sprintf(buf, "flock(%s): %s\n", file, errmsg(errno));
  1630. X            log(L_WARNING, buf);
  1631. X        }
  1632. X        return(FALSE);
  1633. X    }
  1634. X#endif    LOCK_EX
  1635. X#endif    USG
  1636. X    return(TRUE);
  1637. X}
  1638. END_OF_FILE
  1639. if test 26902 -ne `wc -c <'./xmit/nntpxmit.c'`; then
  1640.     echo shar: \"'./xmit/nntpxmit.c'\" unpacked with wrong size!
  1641. fi
  1642. # end of './xmit/nntpxmit.c'
  1643. fi
  1644. echo shar: End of archive 8 \(of 9\).
  1645. cp /dev/null ark8isdone
  1646. MISSING=""
  1647. for I in 1 2 3 4 5 6 7 8 9 ; do
  1648.     if test ! -f ark${I}isdone ; then
  1649.     MISSING="${MISSING} ${I}"
  1650.     fi
  1651. done
  1652. if test "${MISSING}" = "" ; then
  1653.     echo You have unpacked all 9 archives.
  1654.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1655. else
  1656.     echo You still need to unpack the following archives:
  1657.     echo "        " ${MISSING}
  1658. fi
  1659. ##  End of shell archive.
  1660. exit 0
  1661.