1 ;;; emchat.el --- IM client for (S)XEmacs
3 ;; Copyright (C) 2000 - 2011 Steve Youngs
5 ;; Maintainer: Steve Youngs <steve@emchat.org>
6 ;; Created: Aug 08, 1998
7 ;; Homepage: http://www.emchat.org/
10 ;; This file is part of EMchat.
12 ;; Redistribution and use in source and binary forms, with or without
13 ;; modification, are permitted provided that the following conditions
16 ;; 1. Redistributions of source code must retain the above copyright
17 ;; notice, this list of conditions and the following disclaimer.
19 ;; 2. Redistributions in binary form must reproduce the above copyright
20 ;; notice, this list of conditions and the following disclaimer in the
21 ;; documentation and/or other materials provided with the distribution.
23 ;; 3. Neither the name of the author nor the names of any contributors
24 ;; may be used to endorse or promote products derived from this
25 ;; software without specific prior written permission.
27 ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
28 ;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29 ;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30 ;; DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 ;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 ;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 ;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34 ;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
35 ;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
36 ;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
37 ;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ;; Clone of Mirabilis ICQ communication client.
48 ;; See README & INSTALL which come with this package
50 ;; This project is done without the consent of Mirabilis.
56 (require 'emchat-utils)
59 (require 'emchat-doctor))
62 (defvar emchat-add-user-success)
63 (defvar emchat-user-status)
64 (defvar emchat-user-initial-status)
65 (defvar emchat-buddy-buffer)
66 (defvar emchat-buddy-window-width)
67 (defvar emchat-status-buffer)
68 (defvar emchat-status-use-gutter)
69 (defvar emchat-status-window-height)
70 (defvar emchat-wharf-frame)
75 (defvar emchat-fix-nick)
76 (defvar emchat-wharf-frame-use-p)
82 (require 'toolbar-utils)
83 (autoload 'emchat-wharf-dec-messages "emchat-wharf"))
85 (autoload 'emchat-status-auto-reply "emchat-status")
86 (autoload 'emchat-status-idle-reply "emchat-status")
87 (autoload 'emchat-status-name "emchat-status")
88 (autoload 'emchat-change-status "emchat-status" nil t)
89 (autoload 'emchat-status-show-buffer "emchat-status" nil t)
90 (autoload 'emchat-update-tab-in-gutter "emchat-status")
91 (autoload 'emchat-status-v8 "emchat-status")
92 (autoload 'emchat-buddy-update-status "emchat-status")
93 (autoload 'emchat-buddy-selected-in-view "emchat-buddy")
94 (autoload 'emchat-buddy-show-buffer "emchat-buddy" nil t)
95 (autoload 'emchat-buddy-select-all-in-view "emchat-buddy")
100 "Mirabilis ICQ communication client."
103 (defgroup emchat-info nil
104 "Essential account info."
107 (defgroup emchat-option nil
108 "System settings and general preferences."
111 (defgroup emchat-sound nil
115 (defgroup emchat-interface nil
116 "Change the look and \"feel\"."
121 (defcustom emchat-directory (file-name-as-directory
122 (expand-file-name ".emchat" (user-home-directory)))
123 "*All EMchat support files and directories hang off this."
127 ;; Because of the incredibly complex and hairy twisted maze of
128 ;; inter-connections between the different EMchat libs, these
129 ;; emchat-history defcustoms are here instead of in
130 ;; emchat-history.el. --SY.
131 (defgroup emchat-history nil
132 "History preferences."
133 :prefix "emchat-history-"
136 (defcustom emchat-history-enabled-flag nil
137 "*Non-nil means keep \"per-user\" histories."
138 :group 'emchat-history
141 (defcustom emchat-history-directory
142 (file-name-as-directory (expand-file-name "history" emchat-directory))
143 "*Directory path for storing \"per-user\" history files."
145 :group 'emchat-history)
147 (defcustom emchat-history-mode-hook nil
148 "*Hooks run in `emchat-history-mode'."
150 :group 'emchat-history)
152 ;; This is here and not at the top because some of these libs use
155 (require 'emchat-log)
156 (require 'emchat-meta)
157 (require 'emchat-world)
159 (require 'emchat-version))
161 (defcustom emchat-server "login.icq.com"
162 "*Server host to connect to."
166 (defcustom emchat-port 5401
167 "*Port to connect to."
171 ;;;###autoload(autoload 'emchat-prefix "emchat-menu" nil nil 'keymap)
172 (defun emchat-install-bindings (&optional sym value)
173 (when (eq (key-binding (symbol-value sym)) emchat-prefix)
174 (global-set-key (symbol-value sym) nil)) ; unbind old
175 (if (key-binding value)
177 (lwarn 'binding 'warning
178 "%S already bound, reseting `emchat-prefix-key'" value)
180 (global-set-key value emchat-prefix)
183 (defcustom emchat-prefix-key [(meta ?`)]
184 "*Default global prefix key for EMchat.
186 If you change this outside of the customize buffer you _MUST_ use
187 `customize-set-variable', not `setq'."
189 :set 'emchat-install-bindings
190 :initialize 'custom-initialize-default
193 (defcustom emchat-use-sound-flag nil
194 "*Whether to use sound or not."
199 (defcustom emchat-sound-directory
200 (file-name-as-directory (expand-file-name "sounds" emchat-directory))
201 "*Directory where sound files are kept."
204 :tag "emchat-sound-directory")
206 (defcustom emchat-sound-alist
207 '((message-sound . nil)
214 (system-sound . nil))
215 "*Sound event to sound file alist.
216 The possible sound events are:
217 \"message-sound\" - Incoming message sound.
218 \"chat-sound\" - Incoming chat request sound.
219 \"url-sound\" - Incoming url sound.
220 \"buddy-sound\" - Online notify sound.
221 \"auth-sound\" - Authorise sound.
222 \"emailx-sound\" - Email express sound.
223 \"pager-sound\" - Pager sound.
224 \"system-sound\" - System message sound."
227 (cons (sexp :tag "Sound Event")
228 (sexp :tag "Sound File")))
231 (defcustom emchat-coding-system
232 (when (featurep '(or mule file-coding))
233 (if (eq default-buffer-file-coding-system 'cyrillic)
234 (find-coding-system 'windows-1251)
235 default-buffer-file-coding-system))
236 "*Coding for incoming and outgoing messages.
237 This feature is supported only in Emacs with MULE
238 Nil means not to use any codings.
239 See `list-coding-systems'."
240 :group 'emchat-option
241 :type (append '(choice (item nil))
242 (when (fboundp 'coding-system-list)
246 (coding-system-list)))))
248 (defcustom emchat-auto-response-messages-p t
249 "Set this to non-NIL to send automatic messages.
250 The automatic messages are those that are sent when somebody
251 sends you a message while you are 'away', 'na', 'dnd', or 'occ'."
252 :tag "Send auto-response messages."
254 :group 'emchat-option)
256 (defcustom emchat-auto-reply-away
257 "I am currently away from the computer.
259 If you would like to be notified when I am back online
260 send me a message with \",,notify-me\" in it.
262 This message has been automatically sent to you
263 by the (S)XEmacs IM client \"EMchat\".
264 <http://www.emchat.org/>"
265 "Auto reply with this when you are away."
266 :group 'emchat-option)
268 (defcustom emchat-auto-reply-occ
269 "I am currently occupied.
271 If you would like to be notified when I am back online
272 send me a message with \",,notify-me\" in it.
274 This message has been automatically sent to you
275 by the (S)XEmacs IM client \"EMchat\".
276 <http://www.emchat.org/>"
277 "Auto reply with this when you are occupied."
278 :group 'emchat-option)
280 (defcustom emchat-auto-reply-dnd
281 "Hey, the sign on the door says \"Do Not Disturb\"!
283 Leave me a message, if you feel you must.
284 I might get back to you.
286 If you would like to be notified when I am back online
287 send me a message with \",,notify-me\" in it.
289 This message has been automatically sent to you
290 by the (S)XEmacs IM client \"EMchat\".
291 <http://www.emchat.org/>"
292 "Auto reply with this when you want to leave alone."
293 :group 'emchat-option)
295 (defcustom emchat-auto-reply-na
296 "I am currently not available.
298 If you would like to be notified when I am back online
299 send me a message with \",,notify-me\" in it.
301 This message has been automatically sent to you
302 by the (S)XEmacs IM client \"EMchat\".
303 <http://www.emchat.org/>"
304 "Auto reply with this when you are not available."
305 :group 'emchat-option)
307 ;; FIXME: How can I make this display how long we've been away
308 (defcustom emchat-idle-reply-away
309 "I must be too busy to talk because I have
310 been idle now for at least...seconds
312 If you would like to be notified when I am back online
313 send me a message with \",,notify-me\" in it.
315 This message has been automatically sent to you
316 by the (S)XEmacs IM client \"EMchat\".
317 <http://www.emchat.org/>"
318 "Auto reply with this when you have idled away."
319 :group 'emchat-option)
321 ;; FIXME: How can I make this display how long we've been away
322 (defcustom emchat-idle-reply-na
323 "I must be too busy to talk because I have
324 been idle now for at least...seconds
326 If you would like to be notified when I am back online
327 send me a message with \",,notify-me\" in it.
329 This message has been automatically sent to you
330 by the (S)XEmacs IM client \"EMchat\".
331 <http://www.emchat.org/>"
332 "Auto reply with this when you have idled to na."
333 :group 'emchat-option)
335 (defcustom emchat-auto-response-never-send-to nil
336 "*This is a list of people that shouldn't get auto-responses.
338 When you add someone's alias here and they send you a message while
339 your status would cause an automatic response to be sent, they won't
341 :type '(repeat (string :tag "Alias"))
342 :group 'emchat-option)
344 (defcustom emchat-oops-msg-wrong-recipient
345 "That last message was meant for somebody else.
346 Sorry about that. :-)"
347 "*The \"apology\" sent when you send to the wrong person."
349 :group 'emchat-option)
351 (defcustom emchat-start-in-new-frame nil
352 "*If non-NIL, EMchat will start in its own frame."
353 :group 'emchat-interface
356 (defcustom emchat-new-message-hook nil
357 "*Hooks to run when there is an incoming message.
358 Dynamically ALIAS and MESSAGE are binded to be used in hooks."
359 :group 'emchat-option
362 (defcustom emchat-read-message-hook nil
363 "*Hooks run when a message is marked as \"read\"."
364 :group 'emchat-option
367 (defcustom emchat-system-message-hook nil
368 "*Hooks run when a \"system\" message is received."
369 :group 'emchat-option
372 (defcustom emchat-load-hook nil
373 "*Hooks run after EMchat has loaded everything up."
375 :group 'emchat-option)
377 (defcustom emchat-missed-message-hook nil
378 "*Hooks run when SRV_MISSED_ICBM packet comes in.
380 This is usually when you are getting too many incoming messages at
381 once. You can use this hook, for example to send back a \"please
382 resend\" message to the original sender.
384 It is called with 3 arguments:
386 ALIAS -- The alias/UIN of the person who sent the message that
387 caused the SRV_MISSED_ICBM packet to be sent. \(string\)
388 NUM -- The number of missed messages. \(integer\)
389 REASON -- The reason that the messages were dropped. \(string\)"
391 :group 'emchat-option)
393 ;; Some debugging counters. Do NOT set any of these.
394 (defvar emchat-dropped-packet-counter 0
395 "For debug purpose only.")
397 (defvar emchat-resend-packet-counter 0
398 "For debug purpose only.")
400 (defvar emchat-recent-packet nil
401 "The most recent incoming packet.
404 (defvar emchat-trimmed-packet-counter 0
405 "For debug purpose only.")
407 (defvar emchat-error-packets nil
408 "A list of error incoming packets.
411 (defcustom emchat-about-fields
412 '((:nick . "Nick Name")
413 (:first-name . "First Name")
414 (:second-name . "Surname")
416 (:country . "Country")
419 (:zip . "Postal Code")
422 (:cellular . "Cellular")
424 (:web-indicator . "Web Indicator"))
425 "*Alist of field . field-name for basic info queries."
426 :type '(repeat (cons :tag "Field"
427 (choice :tag "Field Keyword"
428 (const :tag "Nick Name" :value :nick)
429 (const :tag "First Name" :value :first-name)
430 (const :tag "Second Name" :value :second-name)
431 (const :tag "Email" :value :email)
432 (const :tag "Country" :value :country)
433 (const :tag "City" :value :city)
434 (const :tag "State" :value :state)
435 (const :tag "Phone" :value :phone)
436 (const :tag "Fax" :value :fax)
437 (const :tag "Street" :value :street)
438 (const :tag "Cellular" :value :cellular)
439 (const :tag "ZIP Code" :value :zip)
440 (const :tag "Flags" :value :flags)
441 (const :tag "Web Indicator" :value :web-indicator))
442 (string :tag "Field Name")))
445 (defcustom emchat-about-more-fields
448 (:homepage . "Homepage")
449 (:birth-year . "Birth Year")
450 (:birth-month . "Birth Month")
451 (:birth-day . "Birth Day")
452 (:lang1 . "Language")
453 (:lang2 . "Second Language")
454 (:lang3 . "Third Language")
455 (:ocity . "Old City")
456 (:ostate . "Old State")
457 (:ocountry . "Old Country")
458 (:marital . "Marital Status"))
459 "*Alist of field . fieldname for extended info queries."
460 :type '(repeat (cons :tag "Field"
461 (choice :tag "Field Keyword"
462 (const :tag "Age" :value :age)
463 (const :tag "Gender" :value :gender)
464 (const :tag "Homepage" :value :homepage)
465 (const :tag "Birth Year" :value :birth-year)
466 (const :tag "Birth Month" :value :birth-month)
467 (const :tag "Birth Day" :value :birth-day)
468 (const :tag "Language" :value :lang1)
469 (const :tag "Second Language" :value :lang2)
470 (const :tag "Third Language" :value :lang3)
471 (const :tag "Originate City" :value :ocity)
472 (const :tag "Originate State" :value :ostate)
473 (const :tag "Originate Country" :value :ocountry)
474 (const :tag "Marital" :value :marital))
475 (string :tag "Documentation")))
478 (defcustom emchat-auth-accept-reason "You are AUTHORISED!"
479 "*Default reason for rejecting incoming auth requests."
483 (defcustom emchat-auth-reject-reason "Authorisation Rejected!"
484 "*Default reason for rejecting incoming auth requests."
488 (defcustom emchat-auth-request-reason "Please add me to your contact list"
489 "*Message to send with outgoing auth requests."
493 (defun emchat-init-visible-list (&rest args)
494 "Initialises the default value for `emchat-visible-contacts'."
495 (when (file-readable-p emchat-world-rc-filename)
496 (emchat-world-update)
502 (defcustom emchat-visible-contacts (emchat-init-visible-list)
503 "*List of contacts on your \"visible\" list."
504 :type '(repeat (string :tag "Contact Alias Name"))
505 :initialize #'custom-initialize-reset
506 :get #'emchat-init-visible-list
507 :set #'custom-set-default
510 (defcustom emchat-invisible-contacts nil
511 "*List of contacts on your \"invisible\" list."
512 :type '(repeat (string :tag "Contact Alias Name"))
515 ;;; Internal variables
516 (defcustom emchat-user-password nil
517 "*Password for your ICQ account.
518 Nil means prompt for entering password every time you login."
521 (defvar emchat-ctx nil
522 "Current emchat context in emchat-v8 protocol.
523 Internal variable, do not modify.")
526 (defun emchat-version (&optional arg)
527 "Return the version of emchat you are currently using.
528 If ARG, insert version string at point."
531 (insert (message "EMchat: %s" emchat-version))
532 (message "EMchat: %s" emchat-version)))
535 (defun emchat-copyright ()
536 "*Display the copyright notice for EMchat."
543 (insert-file-contents (locate-library "emchat.el"))
544 (goto-char (point-min))
545 (re-search-forward ";;; Commentary" nil t)
547 (narrow-to-region (point-min) (point))
548 (while (re-search-backward "^;+ ?" nil t)
549 (replace-match "" nil nil))
550 (buffer-string (current-buffer)))))
551 "*EMchat Copyright Notice*"))
553 (defconst emchat-donation-notice
554 "EMchat is an Open Source project and we have had a lot of fun in
555 getting it into your hands. But this project is NOT a \"for profit\"
556 organisation. We do not receive any funding, Government grants, or
557 subsidies of any kind. None of us who are involved with the project
558 are remunerated in any fashion for what we do with EMchat. We are all
559 just volunteers, coding in our spare time.
561 Often the end user doesn't realise that their \"free\" software has
562 come at some considerable cost. Costs and expenses like...
564 Bandwidth and ISP expenses
565 Hardware updates and maintenance expenses
567 Domain name registrations
568 Electricity and other utility expenses
569 Outrageous amounts of coffee for all-night coding sessions
571 If you have found this software useful/cool/entertaining please
572 consider dipping into your hard earned and making a donation. Doing
573 so will give you the eternal gratitude and thanks from the EMchat
574 team, and think of the warm fuzzies you'll get.
576 Seriously, even if you decide against making a donation at this time,
577 I would like to sincerely thank you for at least taking the time to
578 consider it. I hope you enjoy EMchat as much as we have enjoyed writing
584 \t\t [Donate]\t\t\t [Cancel]
587 "Contents of donation buffer.")
589 (defconst emchat-paypal-glyph
590 "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
635 "A base64 encoded paypal donate button.")
637 (defconst emchat-maybe-later-glyph
638 "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"
681 "A base64 encoded png \"Maybe Later\" button.")
683 (defun emchat-make-donation ()
684 "Proceed with making a donation to the EMchat project."
686 (browse-url "http://tinyurl.com/2uzel4")
687 (kill-buffer "*emchat-donate*"))
689 (defun emchat-no-donation ()
690 "Don't make a donation to the EMchat project."
692 (kill-buffer "*emchat-donate*"))
694 (defconst emchat-donation-map
695 (let* ((map (make-sparse-keymap 'emchat-donation-map)))
696 (define-key map [button1] 'emchat-make-donation)
697 (define-key map [button2] 'emchat-make-donation)
698 (define-key map [button3] 'emchat-make-donation)
699 (define-key map [return] 'emchat-make-donation)
701 "A keymap for the extents in the EMchat donation buffer.")
703 (defconst emchat-nodonation-map
704 (let* ((map (make-sparse-keymap 'emchat-nodonation-map)))
705 (define-key map [button1] 'emchat-no-donation)
706 (define-key map [button2] 'emchat-no-donation)
707 (define-key map [button3] 'emchat-no-donation)
708 (define-key map [return] 'emchat-no-donation)
710 "A keymap for the extents in the EMchat donation buffer.")
712 (defun emchat-donation ()
713 "Make a donation to the EMchat project via PayPal."
715 (let ((buf (get-buffer-create "*emchat-donate*"))
716 (donate-help "Make a donation to the EMchat team.")