]> code.delx.au - gnu-emacs-elpa/blob - packages/debbugs/debbugs.el
* Debbugs.wsdl: Add get_usertag specification.
[gnu-emacs-elpa] / packages / debbugs / debbugs.el
1 ;;; debbugs.el --- SOAP library to access debbugs servers
2
3 ;; Copyright (C) 2011, 2012 Free Software Foundation, Inc.
4
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
6 ;; Keywords: comm, hypermedia
7 ;; Package: debbugs
8 ;; Version: 0.3
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; This package provides some basic functions to access a debbugs SOAP
28 ;; server (see <http://wiki.debian.org/DebbugsSoapInterface>).
29
30 ;; The SOAP function "get_versions" is not implemented (yet).
31
32 ;;; Code:
33
34 ;(setq soap-debug t message-log-max t)
35 (require 'soap-client)
36 (eval-when-compile (require 'cl))
37
38 (defgroup debbugs nil
39 "Debbugs library"
40 :group 'hypermedia)
41
42 (defcustom debbugs-servers
43 '(("gnu.org"
44 :wsdl "http://debbugs.gnu.org/cgi/soap.cgi?WSDL"
45 :bugreport-url "http://debbugs.gnu.org/cgi/bugreport.cgi")
46 ("debian.org"
47 :wsdl "http://bugs.debian.org/cgi-bin/soap.cgi?WSDL"
48 :bugreport-url "http://bugs.debian.org/cgi-bin/bugreport.cgi"))
49 "*List of Debbugs server specifiers.
50 Each entry is a list that contains a string identifying the port
51 name and the server parameters in keyword-value form. Allowed
52 keywords are:
53
54 `:wsdl' -- Location of WSDL. The value is a string with URL that
55 should return the WSDL specification of Debbugs/SOAP service.
56
57 `:bugreport-url' -- URL of the server script that returns mboxes
58 with bug logs.
59
60 The list initially contains two predefined and configured Debbugs
61 servers: \"gnu.org\" and \"debian.org\"."
62 :group 'debbugs
63 :link '(custom-manual "(debbugs)Debbugs server specifiers")
64 :type '(choice
65 (const nil)
66 (repeat
67 (cons :tag "Server"
68 (string :tag "Port name")
69 (checklist :tag "Options" :greedy t
70 (group :inline t
71 (const :format "" :value :wsdl)
72 (string :tag "WSDL"))
73 (group :inline t
74 (const :format "" :value :bugreport-url)
75 (string :tag "Bugreport URL")))))))
76
77 (defcustom debbugs-port "gnu.org"
78 "The port instance to be applied from `debbugs-wsdl'.
79 This corresponds to the Debbugs server to be accessed, either
80 \"gnu.org\", or \"debian.org\", or user defined port name."
81 ;; Maybe we should create an own group?
82 :group 'debbugs
83 :type '(choice :tag "Debbugs server" (const "gnu.org") (const "debian.org")
84 (string :tag "user defined port name")))
85
86 ;; It would be nice if we could retrieve it from the debbugs server.
87 ;; Not supported yet.
88 (defconst debbugs-wsdl
89 (soap-load-wsdl
90 (expand-file-name
91 "Debbugs.wsdl"
92 (if load-in-progress
93 (file-name-directory load-file-name)
94 default-directory)))
95 "The WSDL object to be used describing the SOAP interface.")
96
97 (defun debbugs-get-bugs (&rest query)
98 "Return a list of bug numbers which match QUERY.
99
100 QUERY is a sequence of keyword-value pairs where the values are
101 strings, i.e. :KEYWORD \"VALUE\" [:KEYWORD \"VALUE\"]*
102
103 The keyword-value pair is a subquery. The keywords are allowed to
104 have multiple occurrence within the query at any place. The
105 subqueries with the same keyword form the logical subquery, which
106 returns the union of bugs of every subquery it contains.
107
108 The result of the QUERY is an intersection of results of all
109 subqueries.
110
111 Valid keywords are:
112
113 :package -- The value is the name of the package a bug belongs
114 to, like \"emacs\", \"coreutils\", \"gnus\", or \"tramp\".
115
116 :src -- This is used to retrieve bugs that belong to source
117 with given name.
118
119 :severity -- This is the severity of the bug. The exact set of
120 allowed values depends on the Debbugs port. Examples are
121 \"normal\", \"minor\", \"wishlist\" etc.
122
123 :tag -- An arbitrary string the bug is annotated with.
124 Usually, this is used to mark the status of the bug, like
125 \"fixed\", \"moreinfo\", \"notabug\", \"patch\",
126 \"unreproducible\" or \"wontfix\". The exact set of tags
127 depends on the Debbugs port.
128
129 :owner -- This is used to identify bugs by the owner's email
130 address. The special email address \"me\" is used as pattern,
131 replaced with `user-mail-address'.
132
133 :submitter -- With this keyword it is possible to filter bugs
134 by the submitter's email address. The special email address
135 \"me\" is used as pattern, replaced with `user-mail-address'.
136
137 :maint -- This is used to find bugs of the packages which are
138 maintained by the person with the given email address. The
139 special email address \"me\" is used as pattern, replaced with
140 `user-mail-address'.
141
142 :correspondent -- This allows to find bug reports where the
143 person with the given email address has participated. The
144 special email address \"me\" is used as pattern, replaced with
145 `user-mail-address'.
146
147 :affects -- With this keyword it is possible to find bugs which
148 affect the package with the given name. The bugs are chosen by
149 the value of field `affects' in bug's status. The returned bugs
150 do not necessary belong to this package.
151
152 :status -- Status of bug. Valid values are \"done\",
153 \"forwarded\" and \"open\".
154
155 :archive -- A keyword to filter for bugs which are already
156 archived, or not. Valid values are \"0\" (not archived),
157 \"1\" (archived) or \"both\". If this keyword is not given in
158 the query, `:archive \"0\"' is assumed by default.
159
160 Example. Get all opened and forwarded release critical bugs for
161 the packages which are maintained by \"me\" and which have a
162 patch:
163
164 \(debbugs-get-bugs :maint \"me\" :tag \"patch\"
165 :severity \"critical\"
166 :status \"open\"
167 :severity \"grave\"
168 :status \"forwarded\"
169 :severity \"serious\")"
170
171 (let (vec kw key val)
172 ;; Check query.
173 (while (and (consp query) (<= 2 (length query)))
174 (setq kw (pop query)
175 val (pop query))
176 (unless (and (keywordp kw) (stringp val))
177 (error "Wrong query: %s %s" kw val))
178 (setq key (substring (symbol-name kw) 1))
179 (case kw
180 ((:package :severity :tag :src :affects)
181 ;; Value shall be one word.
182 (if (string-match "\\`\\S-+\\'" val)
183 (setq vec (vconcat vec (list key val)))
184 (error "Wrong %s: %s" key val)))
185 ((:owner :submitter :maint :correspondent)
186 ;; Value is an email address.
187 (if (string-match "\\`\\S-+\\'" val)
188 (progn
189 (when (string-equal "me" val)
190 (setq val user-mail-address))
191 (when (string-match "<\\(.+\\)>" val)
192 (setq val (match-string 1 val)))
193 (setq vec (vconcat vec (list key val))))
194 (error "Wrong %s: %s" key val)))
195 (:status
196 ;; Possible values: "done", "forwarded" and "open"
197 (if (string-match "\\`\\(done\\|forwarded\\|open\\)\\'" val)
198 (setq vec (vconcat vec (list key val)))
199 (error "Wrong %s: %s" key val)))
200 (:archive
201 ;; Value is `0' or `1' or `both'.
202 (if (string-match "\\`\\(0\\|1\\|both\\)\\'" val)
203 (setq vec (vconcat vec (list key val)))
204 (error "Wrong %s: %s" key val)))
205 (t (error "Unknown key: %s" kw))))
206
207 (unless (null query)
208 (error "Unknown key: %s" (car query)))
209 (sort (car (soap-invoke debbugs-wsdl debbugs-port "get_bugs" vec)) '<)))
210
211 (defun debbugs-newest-bugs (amount)
212 "Return the list of bug numbers, according to AMOUNT (a number) latest bugs."
213 (sort (car (soap-invoke debbugs-wsdl debbugs-port "newest_bugs" amount)) '<))
214
215 (defun debbugs-get-status (&rest bug-numbers)
216 "Return a list of status entries for the bugs identified by BUG-NUMBERS.
217
218 Every returned entry is an association list with the following attributes:
219
220 `bug_num': The bug number.
221
222 `package': A list of package names the bug belongs to.
223
224 `severity': The severity of the bug report. This can be
225 \"important\", \"grave\", \"normal\", \"minor\" or \"wishlist\".
226
227 `tags': The status of the bug report, a list of strings. This
228 can be \"fixed\", \"notabug\", \"wontfix\", \"unreproducible\",
229 \"moreinfo\" or \"patch\".
230
231 `pending': The string \"pending\", \"forwarded\" or \"done\".
232
233 `subject': Subject/Title of the bugreport.
234
235 `originator': Submitter of the bugreport.
236
237 `mergedwith': A list of bug numbers this bug was merged with.
238
239 `source': Source package name of the bug report.
240
241 `date': Date of bug creation.
242
243 `log_modified', `last_modified': Date of last update.
244
245 `found_date', `fixed_date': Date of bug report / bug fix
246 \(empty for now).
247
248 `done': The email address of the worker who has closed the bug (if done).
249
250 `archived': `t' if the bug is archived, `nil' otherwise.
251
252 `unarchived': The date the bug has been unarchived, if ever.
253
254 `found_versions', `fixed_versions': List of version strings.
255
256 `forwarded': A URL or an email address.
257
258 `blocks': A list of bug numbers this bug blocks.
259
260 `blockedby': A list of bug numbers this bug is blocked by.
261
262 `msgid': The message id of the initial bug report.
263
264 `owner': Who is responsible for fixing.
265
266 `location': Always the string \"db-h\" or \"archive\".
267
268 `affects': A list of package names.
269
270 `summary': Arbitrary text.
271
272 Example:
273
274 \(debbugs-get-status 10)
275
276 => ;; Attributes with empty values are not shown
277 \(\(\(bug_num . 10)
278 \(source . \"unknown\")
279 \(date . 1203606305.0)
280 \(msgid . \"<87zltuz7eh.fsf@freemail.hu>\")
281 \(severity . \"wishlist\")
282 \(owner . \"Magnus Henoch <mange@freemail.hu>\")
283 \(log_modified . 1261079402.0)
284 \(location . \"db-h\")
285 \(subject . \"url-gw should support HTTP CONNECT proxies\")
286 \(originator . \"Magnus Henoch <mange@freemail.hu>\")
287 \(last_modified . 1271200046.0)
288 \(pending . \"pending\")
289 \(package \"emacs\")))"
290 (when bug-numbers
291 (let ((object
292 (car
293 (soap-invoke
294 debbugs-wsdl debbugs-port "get_status"
295 (apply 'vector bug-numbers)))))
296 (mapcar
297 (lambda (x)
298 (let (y)
299 ;; "archived" is the number 1 or 0.
300 (setq y (assoc 'archived (cdr (assoc 'value x))))
301 (setcdr y (= (cdr y) 1))
302 ;; "found_versions" and "fixed_versions" are lists,
303 ;; containing strings or numbers.
304 (dolist (attribute '(found_versions fixed_versions))
305 (setq y (assoc attribute (cdr (assoc 'value x))))
306 (setcdr y (mapcar
307 (lambda (z) (if (numberp z) (number-to-string z) z))
308 (cdr y))))
309 ;; "mergedwith" is a string, containing blank separated bug numbers.
310 (setq y (assoc 'mergedwith (cdr (assoc 'value x))))
311 (when (stringp (cdr y))
312 (setcdr y (mapcar 'string-to-number (split-string (cdr y) " " t))))
313 ;; "package" is a string, containing comma separated
314 ;; package names. "keywords" and "tags" are strings,
315 ;; containing blank separated package names.
316 (dolist (attribute '(package keywords tags))
317 (setq y (assoc attribute (cdr (assoc 'value x))))
318 (when (stringp (cdr y))
319 (setcdr y (split-string (cdr y) ",\\| " t))))
320 (cdr (assoc 'value x))))
321 object))))
322
323 (defun debbugs-get-usertag (user &rest tags)
324 "Return a list of bug numbers which are tagged by USER.
325
326 USER, a string, is either the email address of the user who has
327 applied a user tag, or a pseudo-user like \"emacs\". Usually,
328 pseudo-users are package names.
329
330 TAGS is a list of strings applied as user tags. The returning
331 bug numbers list is filtered for these tags.
332
333 If TAGS is nil, no bug numbers will be returned but a list of
334 existing tags for USER.
335
336 Example:
337
338 \(debbugs-get-usertag \"emacs\")
339
340 => (\"www\" \"solaris\" \"ls-lisp\" \"cygwin\")
341
342 \(debbugs-get-usertag \"emacs\" \"www\" \"cygwin\")
343
344 => (807 1223 5637)"
345 (when (stringp user)
346 (let ((object
347 (car (soap-invoke debbugs-wsdl debbugs-port "get_usertag" user)))
348 result)
349 (if (null tags)
350 ;; Return the list of existing tags.
351 (mapcar
352 (lambda (x) (symbol-name (car x)))
353 object)
354
355 ;; Return bug numbers.
356 (mapcar
357 (lambda (x)
358 (when (member (symbol-name (car x)) tags)
359 (setq result (append (cdr x) result))))
360 object)
361 (sort result '<)))))
362
363 (defun debbugs-get-bug-log (bug-number)
364 "Return a list of messages related to BUG-NUMBER.
365
366 Every message is an association list with the following attributes:
367
368 `msg_num': The number of the message inside the bug log. The
369 numbers are ascending, newer messages have a higher number.
370
371 `header': The message header lines, as arrived at the bug tracker.
372
373 `body': The message body.
374
375 `attachments' A list of possible attachments, or `nil'. Not
376 implemented yet server side."
377 (car (soap-invoke debbugs-wsdl debbugs-port "get_bug_log" bug-number)))
378
379 (defun debbugs-search-est (&rest query)
380 "Return the result of a full text search according to QUERY.
381
382 QUERY is a sequence of lists of keyword-value pairs where the
383 values are strings or numbers, i.e. :KEYWORD \"VALUE\" [:KEYWORD
384 VALUE]*
385
386 Every sublist of the QUERY forms a hyperestraier condition. A
387 detailed description of hyperestraier conditions can be found at
388 URL `http://fallabs.com/hyperestraier/uguide-en.html#searchcond'.
389
390 The following conditions are possible:
391
392 \[:phrase SEARCH-PHRASE :skip NUMBER :max NUMBER\]
393
394 The string SEARCH-PHRASE forms the search on the database. It
395 contains words to be searched for, combined by operators like
396 AND, ANDNOT and OR. If there is no operator between the words,
397 AND is used by default. The phrase keyword and value can also
398 be omitted, this is useful in combination with other conditions.
399
400 :skip and :max are optional. They specify, how many hits are
401 skipped, and how many maximal hits are returned. This can be
402 used for paged results. Per default, :skip is 0 and :max is 10.
403
404 There must be exactly one such condition.
405
406 \[ATTRIBUTE VALUE+ :operation OPERATION :order ORDER\]
407
408 ATTRIBUTE is one of the following keywords:
409
410 :status -- Status of bug. Valid values are \"done\",
411 \"forwarded\" and \"open\".
412
413 :subject, :@title -- The subject of a message or the title of
414 the bug, a string.
415
416 :date, :@cdate -- The submission or modification dates of a
417 message, a number.
418
419 :submitter, :@author -- The email address of the submitter of a
420 bug or the author of a message belonging to this bug, a string.
421 The special email address \"me\" is used as pattern, replaced
422 with `user-mail-address'.
423
424 :package -- The value is the name of the package a bug belongs
425 to, like \"emacs\", \"coreutils\", \"gnus\", or \"tramp\".
426
427 :tags -- An arbitrary string the bug is annotated with.
428
429 :severity -- This is the severity of the bug. The exact set of
430 allowed values depends on the Debbugs port. Examples are
431 \"normal\", \"minor\", \"wishlist\" etc.
432
433 :operator defines the comparison operator to be applied to
434 ATTRIBUTE. For string attributes this could be \"STREQ\" \(is
435 equal to the string), \"STRNE\" \(is not equal to the string),
436 \"STRINC\" \(includes the string), \"STRBW\" \(begins with the
437 string), \"STREW\" \(ends with the string), \"STRAND\"
438 \(includes all tokens in the string), \"STROR\" \(includes at
439 least one token in the string), \"STROREQ\" \(is equal to at
440 least one token in the string) or \"STRRX\" \(matches regular
441 expressions of the string). For operators with tokens, several
442 values for ATTRIBUTE shall be used.
443
444 Numbers can be compared by the operators \"NUMEQ\" \(is equal
445 to the number), \"NUMNE\" \(is not equal to the number),
446 \"NUMGT\" \(is greater than the number), \"NUMGE\" \(is greater
447 than or equal to the number), \"NUMLT\" \(is less than the
448 number), \"NUMLE\" \(is less than or equal to the number) or
449 \"NUMBT\" \(is between the two numbers). In the last case,
450 there must be two values for ATTRIBUTE.
451
452 If an operator is leaded by \"!\", the meaning is inverted. If
453 a string operator is leaded by \"I\", the case of the value is
454 ignored.
455
456 The optional :order can be specified only in one condition. It
457 means, that ATTRIBUTE is used for sorting the results. The
458 following order operators exist: \"STRA\" \(ascending by
459 string), \"STRD\" \(descending by string), \"NUMA\" \(ascending
460 by number) or \"NUMD\" \(descending by number).
461
462 A special case is an :order, where there is no corresponding
463 attribute value and no operator. In this case, ATTRIBUTE is
464 not used for the search.
465
466 The result of the QUERY is a list of association lists with the
467 same attributes as in the conditions. Additional attributes are
468
469 `id': The bug number.
470
471 `msg_num': The number of the message inside the bug log.
472
473 `snippet': The surrounding text found by the search. For the
474 syntax of the snippet, consult the hyperestraier user guide.
475
476 Examples:
477
478 \(debbugs-search-est
479 '\(:phrase \"armstrong AND debbugs\" :skip 10 :max 2)
480 '\(:severity \"normal\" :operator \"STRINC\")
481 '\(:date :order \"NUMA\"))
482
483 => \(\(\(msg_num . 21)
484 \(date . 1229208302)
485 \(@author . \"Glenn Morris <rgm@gnu.org>\")
486 \(@title . \"Re: bug#1567: Mailing an archived bug\")
487 \(id . 1567)
488 \(severity . \"normal\")
489 \(@cdate . \"Wed, 17 Dec 2008 14:34:50 -0500\")
490 \(snippet . \"...\")
491 \(subject . \"Mailing an archived bug\")
492 \(package . \"debbugs.gnu.org\"))
493 ...)
494
495 ;; Show all messages from me between 2011-08-01 and 2011-08-31.
496 \(debbugs-search-est
497 '\(:max 20)
498 '\(:@author \"me\" :operator \"ISTRINC\")
499 `\(:date
500 ,\(floor \(float-time \(encode-time 0 0 0 1 8 2011)))
501 ,\(floor \(float-time \(encode-time 0 0 0 31 8 2011)))
502 :operator \"NUMBT\"))"
503
504 (let (args result)
505 ;; Compile search arguments.
506 (dolist (elt query)
507 (let (vec kw key val
508 phrase-cond attr-cond)
509
510 ;; Phrase is mandatory, even if empty.
511 (when (and (or (member :skip elt) (member :max elt))
512 (not (member :phrase elt)))
513 (setq vec (vector "phrase" "")))
514
515 ;; Parse condition.
516 (while (consp elt)
517 (setq kw (pop elt))
518 (unless (keywordp kw)
519 (error "Wrong keyword: %s" kw))
520 (setq key (substring (symbol-name kw) 1))
521 (case kw
522 ;; Phrase condition.
523 (:phrase
524 ;; It shouldn't happen in an attribute condition.
525 (if attr-cond
526 (error "Wrong keyword: %s" kw))
527 (setq phrase-cond t val (pop elt))
528 ;; Value is a string.
529 (if (stringp val)
530 (setq vec (vconcat vec (list key val)))
531 (error "Wrong %s: %s" key val)))
532
533 ((:skip :max)
534 ;; It shouldn't happen in an attribute condition.
535 (if attr-cond
536 (error "Wrong keyword: %s" kw))
537 (setq phrase-cond t val (pop elt))
538 ;; Value is a number.
539 (if (numberp val)
540 (setq vec (vconcat vec (list key (number-to-string val))))
541 (error "Wrong %s: %s" key val)))
542
543 ;; Attribute condition.
544 ((:submitter :@author)
545 ;; It shouldn't happen in a phrase condition.
546 (if phrase-cond
547 (error "Wrong keyword: %s" kw))
548 (if (not (stringp (car elt)))
549 (setq vec (vconcat vec (list key "")))
550 ;; Value is an email address.
551 (while (and (stringp (car elt))
552 (string-match "\\`\\S-+\\'" (car elt)))
553 (when (string-equal "me" (car elt))
554 (setcar elt user-mail-address))
555 (when (string-match "<\\(.+\\)>" (car elt))
556 (setcar elt (match-string 1 (car elt))))
557 (add-to-list 'val (pop elt) 'append))
558 (setq vec
559 (vconcat vec (list key (mapconcat 'identity val " "))))))
560
561 (:status
562 ;; It shouldn't happen in a phrase condition.
563 (if phrase-cond
564 (error "Wrong keyword: %s" kw))
565 (setq attr-cond t)
566 (if (not (stringp (car elt)))
567 (setq vec (vconcat vec (list key "")))
568 ;; Possible values: "done", "forwarded" and "open"
569 (while (and (stringp (car elt))
570 (string-match
571 "\\`\\(done\\|forwarded\\|open\\)\\'" (car elt)))
572 (add-to-list 'val (pop elt) 'append))
573 (setq vec
574 (vconcat vec (list key (mapconcat 'identity val " "))))))
575
576 ((:subject :package :tags :severity :@title)
577 ;; It shouldn't happen in a phrase condition.
578 (if phrase-cond
579 (error "Wrong keyword: %s" kw))
580 (setq attr-cond t)
581 (if (not (stringp (car elt)))
582 (setq vec (vconcat vec (list key "")))
583 ;; Just a string.
584 (while (stringp (car elt))
585 (add-to-list 'val (pop elt) 'append))
586 (setq vec
587 (vconcat vec (list key (mapconcat 'identity val " "))))))
588
589 ((:date :@cdate)
590 ;; It shouldn't happen in a phrase condition.
591 (if phrase-cond
592 (error "Wrong keyword: %s" kw))
593 (setq attr-cond t)
594 (if (not (numberp (car elt)))
595 (setq vec (vconcat vec (list key "")))
596 ;; Just a number.
597 (while (numberp (car elt))
598 (add-to-list 'val (pop elt) 'append))
599 (setq vec
600 (vconcat
601 vec (list key (mapconcat 'number-to-string val " "))))))
602
603 ((:operator :order)
604 ;; It shouldn't happen in a phrase condition.
605 (if phrase-cond
606 (error "Wrong keyword: %s" kw))
607 (setq attr-cond t val (pop elt))
608 ;; Value is a number.
609 (if (stringp val)
610 (setq vec (vconcat vec (list key val)))
611 (error "Wrong %s: %s" key val)))
612
613 (t (error "Unknown key: %s" kw))))
614
615 (setq args (vconcat args (list vec)))))
616
617 (setq result
618 (car (soap-invoke debbugs-wsdl debbugs-port "search_est" args)))
619 ;; The result contains lists (key value). We transform it into
620 ;; cons cells (key . value).
621 (dolist (elt1 result result)
622 (dolist (elt2 elt1)
623 (setcdr elt2 (cadr elt2))))))
624
625 (defun debbugs-get-attribute (bug-or-message attribute)
626 "Return the value of key ATTRIBUTE.
627
628 BUG-OR-MESSAGE must be list element returned by either
629 `debbugs-get-status' or `debbugs-get-bug-log'.
630
631 Example: Return the originator of last submitted bug.
632
633 \(debbugs-get-attribute
634 \(car \(apply 'debbugs-get-status \(debbugs-newest-bugs 1))) 'originator)"
635 (cdr (assoc attribute bug-or-message)))
636
637 (defun debbugs-get-message-numbers (messages)
638 "Return the message numbers of MESSAGES.
639 MESSAGES must be the result of a `debbugs-get-bug-log' call."
640 (mapcar (lambda (x) (debbugs-get-attribute x 'msg_num)) messages))
641
642 (defun debbugs-get-message (messages message-number)
643 "Return the message MESSAGE-NUMBER of MESSAGES.
644 MESSAGES must be the result of a `debbugs-get-bug-log' call.
645
646 The returned message is a list of strings. The first element are
647 the header lines of the message, the second element is the body
648 of the message. Further elements of the list, if any, are
649 attachments of the message.
650
651 If there is no message with MESSAGE-NUMBER, the function returns `nil'.
652
653 Example: Return the first message of last submitted bug.
654
655 \(let \(\(messages \(apply 'debbugs-get-bug-log \(debbugs-newest-bugs 1))))
656 \(debbugs-get-message messages
657 \(car \(debbugs-get-message-numbers messages))))"
658 (while (and messages
659 (/= (debbugs-get-attribute (car messages) 'msg_num)
660 message-number))
661 (setq messages (cdr messages)))
662 (when messages
663 (append (list (debbugs-get-attribute (car messages) 'header)
664 (debbugs-get-attribute (car messages) 'body))
665 (debbugs-get-attribute (car messages) 'attachments))))
666
667 (defun debbugs-get-mbox (bug-number mbox-type &optional filename)
668 "Download mbox with messages of bug BUG-NUMBER from Debbugs server.
669 BUG-NUMBER is a number of bug. It must be of integer type.
670
671 MBOX-TYPE specifies a type of mbox and can be one of the
672 following symbols:
673
674 `mboxfolder': Download mbox folder.
675
676 `mboxmaint': Download maintainer's mbox.
677
678 `mboxstat', `mboxstatus': Download status mbox. The use of
679 either symbol depends on actual Debbugs server configuration.
680 For gnu.org, use the former; for debian.org - the latter.
681
682 FILENAME, if non-`nil', is the name of file to store mbox. If
683 FILENAME is `nil', the downloaded mbox is inserted into the
684 current buffer."
685 (let (url (mt "") bn)
686 (unless (setq url (plist-get
687 (cdr (assoc debbugs-port debbugs-servers))
688 :bugreport-url))
689 (error "URL of bugreport script for port %s is not specified"
690 debbugs-port))
691 (setq bn (format "bug=%s;" (number-to-string bug-number)))
692 (unless (eq mbox-type 'mboxfolder)
693 (if (memq mbox-type '(mboxmaint mboxstat mboxstatus))
694 (setq mt (concat (symbol-name mbox-type) "=yes;"))
695 (error "Unknown mbox type: %s" mbox-type)))
696 (setq url (concat url (format "?%s%smbox=yes" bn mt)))
697 (if filename
698 (url-copy-file url filename t)
699 (url-insert-file-contents url))))
700
701 (provide 'debbugs)
702
703 ;;; TODO:
704
705 ;; * SOAP interface extensions (wishlist).
706 ;; - Server-side sorting.
707 ;; - Regexp and/or wildcards search.
708 ;; - Returning message attachments.
709
710 ;;; debbugs.el ends here