| 1 | ;;; rnewspost.el --- USENET news poster/mailer for GNU Emacs |
| 2 | |
| 3 | ;; Copyright (C) 1985, 1986, 1987, 1995 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Maintainer: FSF |
| 6 | ;; Keywords: mail, news |
| 7 | |
| 8 | ;; This file is part of GNU Emacs. |
| 9 | |
| 10 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 11 | ;; it under the terms of the GNU General Public License as published by |
| 12 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 13 | ;; any later version. |
| 14 | |
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | ;; GNU General Public License for more details. |
| 19 | |
| 20 | ;; You should have received a copy of the GNU General Public License |
| 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 22 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 23 | ;; Boston, MA 02111-1307, USA. |
| 24 | |
| 25 | ;;; Change Log: |
| 26 | |
| 27 | ;; moved posting and mail code from rnews.el |
| 28 | ;; tower@gnu.org Wed Oct 29 1986 |
| 29 | ;; brought posting code almost up to the revision of RFC 850 for News 2.11 |
| 30 | ;; - couldn't see handling the special meaning of the Keyword: poster |
| 31 | ;; - not worth the code space to support the old A news Title: (which |
| 32 | ;; Subject: replaced) and Article-I.D.: (which Message-ID: replaced) |
| 33 | ;; tower@gnu.org Nov 86 |
| 34 | ;; changed C-c C-r key-binding due to rename of news-caesar-buffer-body |
| 35 | ;; tower@gnu.org 21 Nov 86 |
| 36 | ;; added (require 'rnews) tower@gnu.org 22 Apr 87 |
| 37 | ;; restricted call of news-show-all-headers in news-post-news & news-reply |
| 38 | ;; tower@gnu.org 28 Apr 87 |
| 39 | ;; commented out Posting-Front-End to save USENET bytes tower@gnu.org Jul 31 87 |
| 40 | ;; commented out -n and -t args in news-inews tower@gnu.org 15 Oct 87 |
| 41 | |
| 42 | ;Now in paths.el. |
| 43 | ;(defvar news-inews-program "inews" |
| 44 | ; "Function to post news.") |
| 45 | |
| 46 | ;; Replying and posting news items are done by these functions. |
| 47 | ;; imported from rmail and modified to work with rnews ... |
| 48 | ;; Mon Mar 25,1985 at 03:07:04 ads@mit-hermes. |
| 49 | ;; this is done so that rnews can operate independently from rmail.el and |
| 50 | ;; sendmail and doesn't have to autoload these functions. |
| 51 | ;; |
| 52 | ;;; >> Nuked by Mly to autoload those functions again, as the duplication of |
| 53 | ;;; >> code was making maintenance too difficult. |
| 54 | |
| 55 | ;;; Commentary: |
| 56 | |
| 57 | ;;; Code: |
| 58 | |
| 59 | (require 'sendmail) |
| 60 | (require 'rnews) |
| 61 | |
| 62 | (defvar news-reply-mode-map () "Mode map used by news-reply.") |
| 63 | |
| 64 | (or news-reply-mode-map |
| 65 | (progn |
| 66 | (setq news-reply-mode-map (make-keymap)) |
| 67 | (define-key news-reply-mode-map "\C-c\C-f\C-d" 'news-reply-distribution) |
| 68 | (define-key news-reply-mode-map "\C-c\C-f\C-k" 'news-reply-keywords) |
| 69 | (define-key news-reply-mode-map "\C-c\C-f\C-n" 'news-reply-newsgroups) |
| 70 | (define-key news-reply-mode-map "\C-c\C-f\C-f" 'news-reply-followup-to) |
| 71 | (define-key news-reply-mode-map "\C-c\C-f\C-s" 'mail-subject) |
| 72 | (define-key news-reply-mode-map "\C-c\C-f\C-a" 'news-reply-summary) |
| 73 | (define-key news-reply-mode-map "\C-c\C-t" 'mail-text) |
| 74 | (define-key news-reply-mode-map "\C-c\C-r" 'news-caesar-buffer-body) |
| 75 | (define-key news-reply-mode-map "\C-c\C-w" 'news-reply-signature) |
| 76 | (define-key news-reply-mode-map "\C-c\C-y" 'news-reply-yank-original) |
| 77 | (define-key news-reply-mode-map "\C-c\C-q" 'mail-fill-yanked-message) |
| 78 | (define-key news-reply-mode-map "\C-c\C-c" 'news-inews) |
| 79 | (define-key news-reply-mode-map "\C-c\C-s" 'news-inews) |
| 80 | (define-key news-reply-mode-map [menu-bar] (make-sparse-keymap)) |
| 81 | (define-key news-reply-mode-map [menu-bar fields] |
| 82 | (cons "Fields" (make-sparse-keymap "Fields"))) |
| 83 | (define-key news-reply-mode-map [menu-bar fields news-reply-distribution] |
| 84 | '("Distribution" . news-reply-distribution)) |
| 85 | (define-key news-reply-mode-map [menu-bar fields news-reply-keywords] |
| 86 | '("Keywords" . news-reply-keywords)) |
| 87 | (define-key news-reply-mode-map [menu-bar fields news-reply-newsgroups] |
| 88 | '("Newsgroups" . news-reply-newsgroups)) |
| 89 | (define-key news-reply-mode-map [menu-bar fields news-reply-followup-to] |
| 90 | '("Followup-to" . news-reply-followup-to)) |
| 91 | (define-key news-reply-mode-map [menu-bar fields mail-subject] |
| 92 | '("Subject" . mail-subject)) |
| 93 | (define-key news-reply-mode-map [menu-bar fields news-reply-summary] |
| 94 | '("Summary" . news-reply-summary)) |
| 95 | (define-key news-reply-mode-map [menu-bar fields mail-text] |
| 96 | '("Text" . mail-text)) |
| 97 | (define-key news-reply-mode-map [menu-bar news] |
| 98 | (cons "News" (make-sparse-keymap "News"))) |
| 99 | (define-key news-reply-mode-map [menu-bar news news-caesar-buffer-body] |
| 100 | '("Rot13" . news-caesar-buffer-body)) |
| 101 | (define-key news-reply-mode-map [menu-bar news news-reply-yank-original] |
| 102 | '("Yank Original" . news-reply-yank-original)) |
| 103 | (define-key news-reply-mode-map [menu-bar news mail-fill-yanked-message] |
| 104 | '("Fill Yanked Messages" . mail-fill-yanked-message)) |
| 105 | (define-key news-reply-mode-map [menu-bar news news-inews] |
| 106 | '("Send" . news-inews)))) |
| 107 | |
| 108 | (defun news-reply-mode () |
| 109 | "Major mode for editing news to be posted on USENET. |
| 110 | First-time posters are asked to please read the articles in newsgroup: |
| 111 | news.announce.newusers . |
| 112 | Like Text Mode but with these additional commands: |
| 113 | |
| 114 | C-c C-s news-inews (post the message) C-c C-c news-inews |
| 115 | C-c C-f move to a header field (and create it if there isn't): |
| 116 | C-c C-f C-n move to Newsgroups: C-c C-f C-s move to Subj: |
| 117 | C-c C-f C-f move to Followup-To: C-c C-f C-k move to Keywords: |
| 118 | C-c C-f C-d move to Distribution: C-c C-f C-a move to Summary: |
| 119 | C-c C-y news-reply-yank-original (insert current message, in NEWS). |
| 120 | C-c C-q mail-fill-yanked-message (fill what was yanked). |
| 121 | C-c C-r caesar rotate all letters by 13 places in the article's body (rot13)." |
| 122 | (interactive) |
| 123 | (kill-all-local-variables) |
| 124 | (make-local-variable 'mail-reply-buffer) |
| 125 | (setq mail-reply-buffer nil) |
| 126 | (set-syntax-table text-mode-syntax-table) |
| 127 | (use-local-map news-reply-mode-map) |
| 128 | (setq local-abbrev-table text-mode-abbrev-table) |
| 129 | (setq major-mode 'news-reply-mode) |
| 130 | (setq mode-name "News Reply") |
| 131 | (make-local-variable 'paragraph-separate) |
| 132 | (make-local-variable 'paragraph-start) |
| 133 | (run-hooks 'text-mode-hook 'news-reply-mode-hook)) |
| 134 | |
| 135 | (defvar news-reply-yank-from "" |
| 136 | "Save `From:' field for `news-reply-yank-original'.") |
| 137 | |
| 138 | (defvar news-reply-yank-message-id "" |
| 139 | "Save `Message-Id:' field for `news-reply-yank-original'.") |
| 140 | |
| 141 | (defun news-reply-yank-original (arg) |
| 142 | "Insert the message being replied to, if any (in Mail mode). |
| 143 | Puts point before the text and mark after. |
| 144 | Indents each nonblank line ARG spaces (default 3). |
| 145 | Just \\[universal-argument] as argument means don't indent |
| 146 | and don't delete any header fields." |
| 147 | (interactive "P") |
| 148 | (mail-yank-original arg) |
| 149 | (exchange-point-and-mark) |
| 150 | (run-hooks 'news-reply-header-hook)) |
| 151 | |
| 152 | (defvar news-reply-header-hook |
| 153 | (lambda () |
| 154 | (insert "In article " news-reply-yank-message-id |
| 155 | " " news-reply-yank-from " writes:\n\n")) |
| 156 | "Hook for inserting a header at the top of a yanked message.") |
| 157 | |
| 158 | (defun news-reply-newsgroups () |
| 159 | "Move point to end of `Newsgroups:' field. |
| 160 | RFC 850 constrains the `Newsgroups:' field to be a comma-separated list |
| 161 | of valid newsgroup names at your site. For example, |
| 162 | Newsgroups: news.misc,comp.misc,rec.misc" |
| 163 | (interactive) |
| 164 | (expand-abbrev) |
| 165 | (goto-char (point-min)) |
| 166 | (mail-position-on-field "Newsgroups")) |
| 167 | |
| 168 | (defun news-reply-followup-to () |
| 169 | "Move point to end of `Followup-To:' field. Create the field if none. |
| 170 | One usually requests followups to only one newsgroup. |
| 171 | RFC 850 constrains the `Followup-To:' field to be a comma-separated list |
| 172 | of valid newsgroups names at your site, and it must be a subset of the |
| 173 | `Newsgroups:' field. For example: |
| 174 | Newsgroups: news.misc,comp.misc,rec.misc,misc.misc,soc.misc |
| 175 | Followup-To: news.misc,comp.misc,rec.misc" |
| 176 | (interactive) |
| 177 | (expand-abbrev) |
| 178 | (or (mail-position-on-field "Followup-To" t) |
| 179 | (progn (mail-position-on-field "newsgroups") |
| 180 | (insert "\nFollowup-To: "))) |
| 181 | ;; @@ could do a completing read based on the Newsgroups: field to |
| 182 | ;; @@ fill in the Followup-To: field |
| 183 | ) |
| 184 | |
| 185 | (defun news-reply-distribution () |
| 186 | "Move point to end of `Distribution:' optional field. |
| 187 | Create the field if none. Without this field the posting goes to all of |
| 188 | USENET. The field is used to restrict the posting to parts of USENET." |
| 189 | (interactive) |
| 190 | (expand-abbrev) |
| 191 | (mail-position-on-field "Distribution") |
| 192 | ;; @@could do a completing read based on the news library file: |
| 193 | ;; @@ ../distributions to fill in the field. |
| 194 | ) |
| 195 | |
| 196 | (defun news-reply-keywords () |
| 197 | "Move point to end of `Keywords:' optional field. Create the field if none. |
| 198 | Used as an aid to the news reader, it can contain a few, well selected keywords |
| 199 | identifying the message." |
| 200 | (interactive) |
| 201 | (expand-abbrev) |
| 202 | (mail-position-on-field "Keywords")) |
| 203 | |
| 204 | (defun news-reply-summary () |
| 205 | "Move point to end of `Summary:' optional field. Create the field if none. |
| 206 | Used as an aid to the news reader, it can contain a succinct |
| 207 | summary (abstract) of the message." |
| 208 | (interactive) |
| 209 | (expand-abbrev) |
| 210 | (mail-position-on-field "Summary")) |
| 211 | |
| 212 | (defun news-reply-signature () |
| 213 | "The inews program appends `~/.signature' automatically." |
| 214 | (interactive) |
| 215 | (message "Posting news will append your signature automatically.")) |
| 216 | |
| 217 | (defun news-setup (to subject in-reply-to newsgroups replybuffer) |
| 218 | "Set up the news reply or posting buffer with the proper headers and mode." |
| 219 | (setq mail-reply-buffer replybuffer) |
| 220 | (let ((mail-setup-hook nil) |
| 221 | ;; Avoid inserting a signature. |
| 222 | (mail-signature)) |
| 223 | (if (null to) |
| 224 | ;; this hack is needed so that inews wont be confused by |
| 225 | ;; the fcc: and bcc: fields |
| 226 | (let ((mail-self-blind nil) |
| 227 | (mail-archive-file-name nil)) |
| 228 | (mail-setup to subject in-reply-to nil replybuffer nil) |
| 229 | (beginning-of-line) |
| 230 | (delete-region (point) (progn (forward-line 1) (point))) |
| 231 | (goto-char (point-max))) |
| 232 | (mail-setup to subject in-reply-to nil replybuffer nil)) |
| 233 | ;;;(mail-position-on-field "Posting-Front-End") |
| 234 | ;;;(insert (emacs-version)) |
| 235 | (goto-char (point-max)) |
| 236 | (if (let ((case-fold-search t)) |
| 237 | (re-search-backward "^Subject:" (point-min) t)) |
| 238 | (progn (beginning-of-line) |
| 239 | (insert "Newsgroups: " (or newsgroups "") "\n") |
| 240 | (if (not newsgroups) |
| 241 | (backward-char 1) |
| 242 | (goto-char (point-max))))) |
| 243 | (let (actual-header-separator) |
| 244 | (rfc822-goto-eoh) |
| 245 | (setq actual-header-separator (buffer-substring |
| 246 | (point) |
| 247 | (save-excursion (end-of-line) (point)))) |
| 248 | (setq paragraph-start |
| 249 | (concat "^" actual-header-separator "$\\|" paragraph-start)) |
| 250 | (setq paragraph-separate |
| 251 | (concat "^" actual-header-separator "$\\|" paragraph-separate))) |
| 252 | (run-hooks 'news-setup-hook))) |
| 253 | |
| 254 | (defun news-inews () |
| 255 | "Send a news message using inews." |
| 256 | (interactive) |
| 257 | (let* (newsgroups subject |
| 258 | (case-fold-search nil)) |
| 259 | (save-excursion |
| 260 | (save-restriction |
| 261 | (narrow-to-region (point-min) (mail-header-end)) |
| 262 | (setq newsgroups (mail-fetch-field "newsgroups") |
| 263 | subject (mail-fetch-field "subject"))) |
| 264 | (widen) |
| 265 | (goto-char (point-min)) |
| 266 | (run-hooks 'news-inews-hook) |
| 267 | (mail-sendmail-undelimit-header) |
| 268 | (goto-char (point-max)) |
| 269 | ;; require a newline at the end for inews to append .signature to |
| 270 | (or (= (preceding-char) ?\n) |
| 271 | (insert ?\n)) |
| 272 | (message "Posting to USENET...") |
| 273 | (unwind-protect |
| 274 | (if (not (eq 0 |
| 275 | (call-process-region (point-min) (point-max) |
| 276 | news-inews-program nil 0 nil |
| 277 | "-h"))) ; take all header lines! |
| 278 | ;@@ setting of subject and newsgroups still needed? |
| 279 | ;"-t" subject |
| 280 | ;"-n" newsgroups |
| 281 | (error "Posting to USENET failed") |
| 282 | (message "Posting to USENET... done")) |
| 283 | (mail-sendmail-delimit-header) |
| 284 | (set-buffer-modified-p nil))) |
| 285 | (bury-buffer))) |
| 286 | |
| 287 | ;@@ shares some code with news-reply and news-post-news |
| 288 | (defun news-mail-reply () |
| 289 | "Mail a reply to the author of the current article. |
| 290 | While composing the reply, use \\[news-reply-yank-original] to yank the |
| 291 | original message into it." |
| 292 | (interactive) |
| 293 | (let (from cc subject date to reply-to message-id |
| 294 | (buffer (current-buffer))) |
| 295 | (save-restriction |
| 296 | (narrow-to-region (point-min) (progn (goto-line (point-min)) |
| 297 | (search-forward "\n\n") |
| 298 | (- (point) 1))) |
| 299 | (setq from (mail-fetch-field "from") |
| 300 | subject (mail-fetch-field "subject") |
| 301 | reply-to (mail-fetch-field "reply-to") |
| 302 | date (mail-fetch-field "date") |
| 303 | message-id (mail-fetch-field "message-id"))) |
| 304 | (setq to from) |
| 305 | (pop-to-buffer "*mail*") |
| 306 | (mail nil |
| 307 | (if reply-to reply-to to) |
| 308 | subject |
| 309 | (let ((stop-pos (string-match " *at \\| *@ \\| *(\\| *<" from))) |
| 310 | (concat (if stop-pos (substring from 0 stop-pos) from) |
| 311 | "'s message " |
| 312 | (if message-id |
| 313 | (concat message-id " of ") |
| 314 | "of ") |
| 315 | date)) |
| 316 | nil |
| 317 | buffer))) |
| 318 | |
| 319 | ;@@ the guts of news-reply and news-post-news should be combined. -tower |
| 320 | (defun news-reply () |
| 321 | "Compose and post a reply (aka a followup) to the current article on USENET. |
| 322 | While composing the followup, use \\[news-reply-yank-original] to yank the |
| 323 | original message into it." |
| 324 | (interactive) |
| 325 | (if (y-or-n-p "Are you sure you want to followup to all of USENET? ") |
| 326 | (let (from cc subject date to followup-to newsgroups message-of |
| 327 | references distribution message-id |
| 328 | (buffer (current-buffer))) |
| 329 | (save-restriction |
| 330 | (and (not (= 0 (buffer-size))) ;@@real problem is non-existence of |
| 331 | ;@@ of article file |
| 332 | (equal major-mode 'news-mode) ;@@ if rmail-mode, |
| 333 | ;@@ should show full headers |
| 334 | (progn |
| 335 | (news-show-all-headers) ;@@ should save/restore header state, |
| 336 | ;@@ but rnews.el lacks support |
| 337 | (narrow-to-region (point-min) (progn (goto-char (point-min)) |
| 338 | (search-forward "\n\n") |
| 339 | (- (point) 1))))) |
| 340 | (setq from (mail-fetch-field "from") |
| 341 | news-reply-yank-from from |
| 342 | ;; @@ not handling old Title: field |
| 343 | subject (mail-fetch-field "subject") |
| 344 | date (mail-fetch-field "date") |
| 345 | followup-to (mail-fetch-field "followup-to") |
| 346 | newsgroups (or followup-to |
| 347 | (mail-fetch-field "newsgroups")) |
| 348 | references (mail-fetch-field "references") |
| 349 | ;; @@ not handling old Article-I.D.: field |
| 350 | distribution (mail-fetch-field "distribution") |
| 351 | message-id (mail-fetch-field "message-id") |
| 352 | news-reply-yank-message-id message-id) |
| 353 | (pop-to-buffer "*post-news*") |
| 354 | (news-reply-mode) |
| 355 | (if (and (buffer-modified-p) |
| 356 | (not |
| 357 | (y-or-n-p "Unsent article being composed; erase it? "))) |
| 358 | () |
| 359 | (progn |
| 360 | (erase-buffer) |
| 361 | (and subject |
| 362 | (progn (if (string-match "\\`Re: " subject) |
| 363 | (while (string-match "\\`Re: " subject) |
| 364 | (setq subject (substring subject 4)))) |
| 365 | (setq subject (concat "Re: " subject)))) |
| 366 | (and from |
| 367 | (progn |
| 368 | (let ((stop-pos |
| 369 | (string-match " *at \\| *@ \\| *(\\| *<" from))) |
| 370 | (setq message-of |
| 371 | (concat |
| 372 | (if stop-pos (substring from 0 stop-pos) from) |
| 373 | "'s message " |
| 374 | (if message-id |
| 375 | (concat message-id " of ") |
| 376 | "of ") |
| 377 | date))))) |
| 378 | (news-setup |
| 379 | nil |
| 380 | subject |
| 381 | message-of |
| 382 | newsgroups |
| 383 | buffer) |
| 384 | (if followup-to |
| 385 | (progn (news-reply-followup-to) |
| 386 | (insert followup-to))) |
| 387 | (if distribution |
| 388 | (progn |
| 389 | (mail-position-on-field "Distribution") |
| 390 | (insert distribution))) |
| 391 | (mail-position-on-field "References") |
| 392 | (if references |
| 393 | (insert references)) |
| 394 | (if (and references message-id) |
| 395 | (insert " ")) |
| 396 | (if message-id |
| 397 | (insert message-id)) |
| 398 | (goto-char (point-max)))))) |
| 399 | (message ""))) |
| 400 | |
| 401 | ;@@ the guts of news-reply and news-post-news should be combined. -tower |
| 402 | ;;;###autoload |
| 403 | (defun news-post-news (&optional noquery) |
| 404 | "Begin editing a new USENET news article to be posted. |
| 405 | Type \\[describe-mode] once editing the article to get a list of commands. |
| 406 | If NOQUERY is non-nil, we do not query before doing the work." |
| 407 | (interactive) |
| 408 | (if (or noquery |
| 409 | (y-or-n-p "Are you sure you want to post to all of USENET? ")) |
| 410 | (let ((buffer (current-buffer))) |
| 411 | (save-restriction |
| 412 | (and (not (= 0 (buffer-size))) ;@@real problem is non-existence of |
| 413 | ;@@ of article file |
| 414 | (equal major-mode 'news-mode) ;@@ if rmail-mode, |
| 415 | ;@@ should show full headers |
| 416 | (progn |
| 417 | (news-show-all-headers) ;@@ should save/restore header state, |
| 418 | ;@@ but rnews.el lacks support |
| 419 | (narrow-to-region (point-min) (progn (goto-char (point-min)) |
| 420 | (search-forward "\n\n") |
| 421 | (- (point) 1))))) |
| 422 | (setq news-reply-yank-from (mail-fetch-field "from") |
| 423 | ;; @@ not handling old Article-I.D.: field |
| 424 | news-reply-yank-message-id (mail-fetch-field "message-id"))) |
| 425 | (pop-to-buffer "*post-news*") |
| 426 | (news-reply-mode) |
| 427 | (if (and (buffer-modified-p) |
| 428 | (not (y-or-n-p "Unsent article being composed; erase it? "))) |
| 429 | () ;@@ not saving point from last time |
| 430 | (progn (erase-buffer) |
| 431 | (news-setup () () () () buffer)))) |
| 432 | (message ""))) |
| 433 | |
| 434 | (defun news-mail-other-window () |
| 435 | "Send mail in another window. |
| 436 | While composing the message, use \\[news-reply-yank-original] to yank the |
| 437 | original message into it." |
| 438 | (interactive) |
| 439 | (mail-other-window nil nil nil nil nil (current-buffer))) |
| 440 | |
| 441 | (provide 'rnewspost) |
| 442 | |
| 443 | ;;; rnewspost.el ends here |