source: tags/0.8.0/parse-message.lisp

Last change on this file was 160, checked in by Erik Huelsmann, 18 years ago

Change Source to URL keyword (remainder of CVS to svn conversion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision URL
File size: 7.5 KB
Line 
1;;;; $Id: parse-message.lisp 160 2006-05-22 20:21:38Z ehuelsmann $
2;;;; $URL: tags/0.8.0/parse-message.lisp $
3
4;;;; See the LICENSE file for licensing information.
5
6(in-package :irc)
7
8(defun find-reply-name (reply-number &key (reply-names *reply-names*))
9  "Numeric replies in the IRC RFCs have more meaningful names.  Given
10a numeric reply (`reply-number') this function will either return the
11symbol representing the reply or raise a continuable error
12(`no-such-reply') which gives you the opportunity to ignore the
13situation."
14  (let ((name (assoc reply-number reply-names)))
15    (if name
16        (cadr name)
17        (progn
18          (cerror "Ignore unknown reply."
19                  'no-such-reply :reply-number reply-number)
20          :unknown-reply))))
21
22(defun return-source (string &key (start 0))
23  "Assuming `string' is a valid IRC message this function returns the
24source part of the message.  Returns nil if the source part is not
25present."
26  (cut-between string #\: '(#\! #\Space) :start start))
27
28(defun return-user (string &key (start 0))
29  "Assuming `string' is a valid IRC message this function returns the
30user part of the message.  Returns nil if the user part is not
31present."
32  (cut-between string #\! '(#\@ #\Space) :start start))
33
34(defun return-host (string &key (start 0))
35  "Assuming `string' is a valid IRC message this function returns the
36host part of the message.  Returns nil if the host part is not
37present."
38  (cut-between string #\@ '(#\Space) :start start))
39
40(defun return-command (string &key (start 0))
41  "Assuming `string' is a valid IRC message this function returns the
42command part of the message.  Returns nil if the command part is not
43present."
44  (if (eql (char string start) #\Space)
45      (cut-between string #\Space '(#\Space) :start start)
46      (cut-between string nil '(#\Space) :start start :cut-extra nil)))
47
48(defun return-arguments (string &key (start 0))
49  "Assuming `string' is a valid IRC message this function returns the
50arguments part of the message as a list.  Returns nil if the arguments
51part is not present."
52  (multiple-value-bind (end-position return-argument)
53      (cut-before string " :" '(#\Return) :start start :cut-to-end t)
54    (values end-position (tokenize-string return-argument
55                                          :delimiters '(#\Space)))))
56
57(defun return-trailing-argument (string &key (start 0))
58  "Assuming `string' is a valid IRC message this function returns the
59trailing-argument part of the message.  Returns nil if the
60trailing-argument part is not present."
61  (when (< start (length string))
62    (cut-between string #\: '(#\Return) :start start :cut-to-end t)))
63
64(defun combine-arguments-and-trailing (string &key (start 0))
65  (multiple-value-bind
66      (start return-string)
67      (return-arguments string :start start)
68    (multiple-value-bind
69        (return-index trailing)
70        (return-trailing-argument string :start start)
71      (values return-index
72              (append return-string (when (and trailing (string/= "" trailing))
73                                      (list trailing)))))))
74
75(defun parse-raw-message (string &key (start 0))
76  "Assuming `string' is a valid IRC message, parse the message and
77return the values in the following order:
78
79  - source
80  - user
81  - host
82  - command
83  - arguments
84  - trailing-argument
85
86Any values not present will be represented as nil."
87  (let ((index start)
88        (returns nil))
89    (dolist (function '(return-source
90                        return-user
91                        return-host
92                        return-command
93                        combine-arguments-and-trailing))
94      (multiple-value-bind (return-index return-string)
95          (funcall function string :start index)
96        (setf index return-index)
97        (push return-string returns)))
98    (apply #'values (reverse returns))))
99
100(defun irc-error-reply-p (string)
101  "Returns t if `string' is a string-representation of an IRC error
102reply message, nil otherwise."
103  (unless (zerop (length string))
104    (if (and (every #'digit-char-p string)
105             (member (char string 0) '(#\4 #\5)))
106        t
107        nil)))
108
109(defun numeric-reply-p (string)
110  "Returns t if `string' is a string-representation of an IRC number
111reply, nil otherwise."
112  (every #'digit-char-p string))
113
114(defun ctcp-type-p (string type)
115  "Is the `string' actually a representation of the CTCP `type'?"
116  (if (string-equal (substring string 1 (min (length string)
117                                             (1+ (length (symbol-name type)))))
118                    type)
119      type
120      nil))
121                                                       
122(defun dcc-type-p (string type)
123  "Is the `string' actually a representation of the DCC `type'?"
124  (case type
125    (:dcc-chat-request
126     (when (string-equal (char string 5) #\C)
127       :dcc-chat-request))
128    (:dcc-send-request
129     (when (string-equal (char string 5) #\S)
130       :dcc-send-request))
131    (otherwise nil)))
132
133(defun ctcp-message-type (string)
134  "If `string' is a CTCP message, return the type of the message or
135nil if this is a) not a CTCP message or b) a CTCP message we don't
136know about."
137  (if (or (not (stringp string))
138          (zerop (length string))
139          (not (eql (char string 0) +soh+)))
140      nil
141      (case (char string 1)
142        (#\A (ctcp-type-p string :action))
143        (#\C (ctcp-type-p string :clientinfo))
144        (#\D
145         (or (dcc-type-p string :dcc-chat-request)
146             (dcc-type-p string :dcc-send-request)))
147        (#\F (ctcp-type-p string :finger))
148        (#\P (ctcp-type-p string :ping))
149        (#\S (ctcp-type-p string :source))
150        (#\T (ctcp-type-p string :time))
151        (#\U (ctcp-type-p string :userinfo))
152        (#\V (ctcp-type-p string :version))
153        (otherwise nil))))
154
155(defun create-irc-message (string)
156  "If `string' is a valid IRC message parse it and return an object of
157the correct type with its slots prefilled according to the information
158in the message."
159  (multiple-value-bind (source user host command arguments)
160      (parse-raw-message string)
161    (let* ((class 'irc-message)
162           (trailing-argument (car (last arguments)))
163           (ctcp (ctcp-message-type trailing-argument)))
164      (when command
165        (cond
166          ;;((irc-error-reply-p command)
167           ;; Disable for now, as it prevents adding hooks for some useful
168           ;; error types
169           ;;(progn
170           ;;  (setf command (find-reply-name (parse-integer command)))
171           ;;  (setf class 'irc-error-reply)))
172          ((numeric-reply-p command)
173           (progn
174             (setf command (find-reply-name (parse-integer command)))
175             (setf class (find-irc-message-class command))))
176          (t
177           (progn
178             (setf command (intern (string-upcase command)
179                                   (find-package :keyword)))
180             (setf class (find-irc-message-class command))))))
181      (when ctcp
182        (setf class (find-ctcp-message-class ctcp)))
183      (let ((instance (make-instance class
184                                     :source (or source "")
185                                     :user (or user "")
186                                     :host (or host "")
187                                     :command (if command
188                                                  (string command)
189                                                  "")
190                                     :arguments arguments
191                                     :connection nil
192                                     :received-time (get-universal-time)
193                                     :raw-message-string (or string ""))))
194        (when ctcp
195          (setf (ctcp-command instance) ctcp))
196        instance))))
Note: See TracBrowser for help on using the repository browser.