This file is indexed.

/usr/share/emacs/site-lisp/wl/elmo/modb-entity.el is in wl-beta 2.15.9+0.20130701-4.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
;;; modb-entity.el --- Message Entity Interface.

;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>

;; Author: Yuuichi Teranishi <teranisi@gohome.org>
;;	Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
;; Keywords: mail, net news

;; This file is part of ELMO (Elisp Library for Message Orchestration).

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;

;;; Commentary:
;;
;; Message entity handling.

;;; Code:

(eval-when-compile (require 'cl))

(require 'luna)
(require 'elmo-vars)
(require 'elmo-util)

(eval-and-compile
  (luna-define-class modb-entity-handler () (mime-charset))
  (luna-define-internal-accessors 'modb-entity-handler))

(defcustom modb-entity-default-handler 'modb-legacy-entity-handler
  "Default entity handler."
  :type 'symbol
  :group 'elmo)

(defcustom modb-entity-field-extractor-alist
  '((ml-info modb-entity-extract-mailing-list-info
	     modb-entity-ml-info-real-fields))
  "*An alist of field name and function to extract field body from buffer."
  :type '(repeat (list (symbol :tag "Field Name")
		       (function :tag "Extractor")
		       (choice :tag "Real Field"
			       (repeat :tag "Field Name List" string)
			       (function :tag "Function"))))
  :group 'elmo)

(defvar modb-entity-default-cache-internal nil)

(defun elmo-message-entity-handler (&optional entity)
  "Get modb entity handler instance which corresponds to the ENTITY."
  (if (and entity
	   (car-safe entity)
	   (not (eq (car entity) t))
	   (not (stringp (car entity))))
      (car entity)
    (or modb-entity-default-cache-internal
	(setq modb-entity-default-cache-internal
	      (luna-make-entity modb-entity-default-handler)))))

(luna-define-generic modb-entity-handler-list-parameters (handler)
  "Return a parameter list of HANDLER.")

(luna-define-generic elmo-msgdb-make-message-entity (handler &rest args)
  "Make a message entity using HANDLER.")

(luna-define-generic elmo-msgdb-message-entity-number (handler entity)
  "Number of the ENTITY.")

(luna-define-generic elmo-msgdb-message-entity-set-number (handler
							   entity number)
  "Set number of the ENTITY.")

(luna-define-generic elmo-msgdb-message-entity-field (handler entity field
							      &optional type)
  "Retrieve field value of the message entity.
HANDLER is the message entity handler.
ENTITY is the message entity structure.
FIELD is a symbol of the field.
If optional argument TYPE is specified, return converted value.")

(luna-define-generic elmo-msgdb-message-entity-set-field (handler
							  entity field value)
  "Set the field value of the message entity.
HANDLER is the message entity handler.
ENTITY is the message entity structure.
FIELD is a symbol of the field.
VALUE is the field value to set.")

(luna-define-generic elmo-msgdb-message-entity-update-fields (handler
							      entity values)
  "Update message entity by VALUES.
HANDLER is the message entity handler.
ENTITY is the message entity structure.
VALUES is an alist of field-name and field-value.")

(luna-define-generic elmo-msgdb-copy-message-entity (handler entity
							     &optional
							     make-handler)
  "Copy message entity.
HANDLER is the message entity handler.
ENTITY is the message entity structure.
If optional argument MAKE-HANDLER is specified, use it to make new entity.")

(luna-define-generic elmo-msgdb-create-message-entity-from-file (handler
								 number
								 file)
  "Create message entity from file.
HANDLER is the message entity handler.
NUMBER is the number of the newly created message entity.
FILE is the message file.")

(luna-define-generic elmo-msgdb-create-message-entity-from-header (handler
								   number
								   &rest args)
  "Create message entity from current buffer.
HANDLER is the message entity handler.
NUMBER is the number of the newly created message entity.
Rest of the ARGS is a plist of message entity field for initial value.
Header region is supposed to be narrowed.")

;; Transitional interface.
(luna-define-generic elmo-msgdb-message-match-condition (handler
							 condition
							 entity)
  "Return non-nil when the entity matches the condition.")

;; Generic implementation.
(luna-define-method initialize-instance :after ((handler modb-entity-handler)
						&rest init-args)
  (unless (modb-entity-handler-mime-charset-internal handler)
    (modb-entity-handler-set-mime-charset-internal handler elmo-mime-charset))
  handler)

(luna-define-method modb-entity-handler-list-parameters
  ((handler modb-entity-handler))
  (list 'mime-charset))

(luna-define-method elmo-msgdb-create-message-entity-from-file
  ((handler modb-entity-handler) number file)
  (when (file-exists-p file)
    (with-temp-buffer
      (setq buffer-file-name file)
      ;; insert header from file.
      (catch 'done
	(condition-case nil
	    (elmo-msgdb-insert-file-header file)
	  (error (setq buffer-file-name nil)
		 (throw 'done nil)))
	(prog1
	    (elmo-msgdb-create-message-entity-from-header
	     handler number)
	  (setq buffer-file-name nil))))))

(luna-define-method elmo-msgdb-make-message-entity ((handler
						     modb-entity-handler)
						    args)
  (cons handler args))

(luna-define-method elmo-msgdb-message-entity-field ((handler
						     modb-entity-handler)
						     entity field
						     &optional type)
  (plist-get (cdr entity) (intern (concat ":" (symbol-name field)))))

(luna-define-method elmo-msgdb-message-entity-number ((handler
						       modb-entity-handler)
						      entity)
  (plist-get (cdr entity) :number))

(luna-define-method elmo-msgdb-message-entity-update-fields
  ((handler modb-entity-handler) entity values)
  (let (updated)
    (dolist (pair values)
      (unless (equal
	       (cdr pair)
	       (elmo-msgdb-message-entity-field handler entity (car pair)))
	(elmo-msgdb-message-entity-set-field handler entity
					     (car pair) (cdr pair))
	(setq updated t)))
    updated))

;; helper functions
(defsubst modb-entity-handler-mime-charset (handler)
  (or (modb-entity-handler-mime-charset-internal handler)
      elmo-mime-charset))

(defun modb-entity-handler-equal-p (handler other)
  "Return non-nil, if OTHER hanlder is equal this HANDLER."
  (and (eq (luna-class-name handler)
	   (luna-class-name other))
       (catch 'mismatch
	 (dolist (slot (modb-entity-handler-list-parameters handler))
	   (when (not (equal (luna-slot-value handler slot)
			     (luna-slot-value other slot)))
	     (throw 'mismatch nil)))
	 t)))

(defun modb-entity-handler-dump-parameters (handler)
  "Return parameters for reconstruct HANDLER as plist."
  (apply #'nconc
	 (mapcar (lambda (slot)
		   (let ((value (luna-slot-value handler slot)))
		     (when value
		       (list (intern (concat ":" (symbol-name slot)))
			     value))))
	 (modb-entity-handler-list-parameters handler))))

;; field in/out converter
(defun modb-set-field-converter (converter type &rest specs)
  "Set convert function of TYPE into CONVERTER.
SPECS must be like `FIELD1 FUNCTION1 FIELD2 FUNCTION2 ...'.
If each field is t, function is set as default converter."
  (when specs
    (let ((alist (symbol-value converter))
	  (type (or type t)))
      (while specs
	(let ((field (pop specs))
	      (function (pop specs))
	      cell)
	  (if (setq cell (assq type alist))
	      (setcdr cell (put-alist field function (cdr cell)))
	    (setq cell  (cons type (list (cons field function)))
		  alist (cons cell alist)))
	  ;; support colon keyword (syntax sugar).
	  (unless (or (eq field t)
		      (string-match "^:" (symbol-name field)))
	    (setcdr cell (put-alist (intern (concat ":" (symbol-name field)))
				    function
				    (cdr cell))))))
      (set converter alist))))
(put 'modb-set-field-converter 'lisp-indent-function 2)

(defsubst modb-convert-field-value (converter field value &optional type)
  (and value
       (let* ((alist (cdr (assq (or type t) converter)))
	      (function (cdr (or (assq field alist)
				 (assq t alist)))))
	 (if function
	     (funcall function field value)
	   value))))

;; mime decode cache
(defvar elmo-msgdb-decoded-cache-hashtb nil)
(make-variable-buffer-local 'elmo-msgdb-decoded-cache-hashtb)

(defsubst elmo-msgdb-get-decoded-cache (string)
  (if elmo-use-decoded-cache
      (let ((hashtb (or elmo-msgdb-decoded-cache-hashtb
			(setq elmo-msgdb-decoded-cache-hashtb
			      (elmo-make-hash 2048))))
	    decoded)
	(or (elmo-get-hash-val string hashtb)
	    (prog1
		(setq decoded
		      (elmo-mime-charset-decode-string
		       string elmo-mime-charset))
	      (elmo-set-hash-val string decoded hashtb))))
    (elmo-mime-charset-decode-string string elmo-mime-charset)))

(defun modb-entity-string-decoder (field value)
  (elmo-msgdb-get-decoded-cache value))

(defun modb-entity-string-encoder (field value)
  (elmo-mime-charset-encode-string value elmo-mime-charset))

(defun modb-entity-parse-date-string (field value)
  (if (stringp value)
      (elmo-time-parse-date-string value)
    value))

(defun modb-entity-make-date-string (field value)
  (if (stringp value)
      value
    (elmo-time-make-date-string value)))

(defun modb-entity-mime-decoder (field value)
  (mime-decode-field-body value (symbol-name field) 'summary))

(defun modb-entity-mime-encoder (field value)
  (mime-encode-field-body value (symbol-name field)))

(defun modb-entity-address-list-decoder (field value)
  (if (stringp value)
      (mapcar (lambda (address)
		(mime-decode-field-body address (symbol-name field)))
	      (elmo-parse-addresses value))
    value))

(defun modb-entity-address-list-encoder (field value)
  (if (stringp value)
      value
    (mime-encode-field-body (mapconcat 'identity value ", ")
			    (symbol-name field))))

(defun modb-entity-parse-address-string (field value)
  (modb-entity-encode-string-recursive
   field
   (if (stringp value)
       (elmo-parse-addresses value)
     value)))

(defun modb-entity-make-address-string (field value)
  (let ((value (modb-entity-decode-string-recursive field value)))
    (if (stringp value)
	value
      (mapconcat 'identity value ", "))))

(defun modb-entity-decode-string-recursive (field value)
  (elmo-map-recursive
   (lambda (element)
     (if (stringp element)
	 (elmo-msgdb-get-decoded-cache element)
       element))
   value))

(defun modb-entity-encode-string-recursive (field value)
  (elmo-map-recursive
   (lambda (element)
     (if (stringp element)
	 (elmo-mime-charset-encode-string element elmo-mime-charset)
       element))
   value))

(defun modb-entity-create-field-indices (slots)
  (let ((index 0)
	indices)
    (while slots
      (setq indices (cons (cons (car slots) index) indices)
	    index   (1+ index)
	    slots   (cdr slots)))
    (append
     indices
     (mapcar (lambda (cell)
	       (cons (intern (concat ":" (symbol-name (car cell))))
		     (cdr cell)))
	     indices))))


;; Legacy implementation.
(eval-and-compile
  (luna-define-class modb-legacy-entity-handler (modb-entity-handler)))

(defconst modb-legacy-entity-field-slots
 '(number
   references
   from
   subject
   date
   to
   cc
   size
   extra))

(defconst modb-legacy-entity-field-indices
  (modb-entity-create-field-indices modb-legacy-entity-field-slots))

(defvar modb-legacy-entity-normalizer nil)
(modb-set-field-converter 'modb-legacy-entity-normalizer nil
  'message-id	nil
  'number	nil
  'references	nil
  'from		#'modb-entity-string-encoder
  'subject	#'modb-entity-string-encoder
  'date		#'modb-entity-make-date-string
  'to		#'modb-entity-address-list-encoder
  'cc		#'modb-entity-address-list-encoder
  'size		nil
  t		#'modb-entity-mime-encoder)

(defvar modb-legacy-entity-specializer nil)
;; default type
(modb-set-field-converter 'modb-legacy-entity-specializer nil
  'message-id	nil
  'number	nil
  'references	nil
  'from		#'modb-entity-string-decoder
  'subject	#'modb-entity-string-decoder
  'date		#'modb-entity-parse-date-string
  'to		#'modb-entity-address-list-decoder
  'cc		#'modb-entity-address-list-decoder
  'size		nil
  t		#'modb-entity-mime-decoder)
;; string type
(modb-set-field-converter 'modb-legacy-entity-specializer 'string
  'message-id	nil
  'number	nil			; not supported
  'references	nil
  'from		#'modb-entity-string-decoder
  'subject	#'modb-entity-string-decoder
  'date		nil
  'size		nil			; not supported
  t		#'modb-entity-mime-decoder)


(defmacro modb-legacy-entity-field-index (field)
  `(cdr (assq ,field modb-legacy-entity-field-indices)))

(defsubst modb-legacy-entity-set-field (entity field value &optional as-is)
  (when entity
    (let (index)
      (unless as-is
	(setq value (modb-convert-field-value
		     modb-legacy-entity-normalizer
		     field value)))
      (cond ((memq field '(message-id :message-id))
	     (setcar entity value))
	    ((setq index (modb-legacy-entity-field-index field))
	     (aset (cdr entity) index value))
	    (t
	     (setq index (modb-legacy-entity-field-index :extra))
	     (let ((extras (and entity (aref (cdr entity) index)))
		   extra)
	       (if (setq extra (assoc (symbol-name field) extras))
		   (setcdr extra value)
		 (aset (cdr entity) index (cons (cons (symbol-name field)
						      value) extras)))))))))

(defsubst modb-legacy-make-message-entity (args)
  "Make an message entity."
  (let ((entity (cons nil (make-vector 9 nil)))
	field value)
    (while args
      (setq field (pop args)
	    value (pop args))
      (when value
	(modb-legacy-entity-set-field entity field value)))
    entity))

(luna-define-method elmo-msgdb-make-message-entity
  ((handler modb-legacy-entity-handler) args)
  (modb-legacy-make-message-entity args))

(luna-define-method elmo-msgdb-create-message-entity-from-header
  ((handler modb-legacy-entity-handler) number args)
  (let ((extras elmo-msgdb-extra-fields)
	(default-mime-charset default-mime-charset)
	entity message-id references from subject to cc date
	extra field-body charset size file-attrib)
    (save-excursion
      (setq entity (modb-legacy-make-message-entity args))
      (set-buffer-multibyte default-enable-multibyte-characters)
      (setq message-id (elmo-msgdb-get-message-id-from-header))
      (and (setq charset (cdr (assoc "charset" (mime-read-Content-Type))))
	   (setq charset (intern-soft charset))
	   (setq default-mime-charset charset))
      (setq references
	    (elmo-msgdb-get-references-from-header)
	    from (elmo-replace-in-string
		  (elmo-mime-string (or (std11-fetch-field "from")
					elmo-no-from))
		  "\t" " ")
	    subject (elmo-replace-in-string
		     (elmo-mime-string (or (std11-fetch-field "subject")
					   elmo-no-subject))
		     "\t" " ")
	    date (or (elmo-decoded-fetch-field "date")
		     (when buffer-file-name
		       (timezone-make-date-arpa-standard
			(current-time-string
			 (nth 5 (or file-attrib
				    (setq file-attrib
					  (file-attributes buffer-file-name)))))
			(current-time-zone))))
	    to   (mapconcat 'identity (elmo-multiple-field-body "to") ",")
	    cc   (mapconcat 'identity (elmo-multiple-field-body "cc") ","))
      (unless (elmo-msgdb-message-entity-field handler entity 'size)
	(setq size
	      (or (std11-fetch-field "content-length")
		  (when buffer-file-name
		    (nth 7 (or file-attrib
			       (setq file-attrib
				     (file-attributes buffer-file-name)))))
		  0))
	(when (stringp size)
	  (setq size (string-to-number size))))
      (while extras
	(if (setq field-body (std11-fetch-field (car extras)))
	    (modb-legacy-entity-set-field
	     entity (intern (downcase (car extras))) field-body 'as-is))
	(setq extras (cdr extras)))
      (dolist (field '(message-id number references from subject
				  date to cc size))
	(when (symbol-value field)
	  (modb-legacy-entity-set-field
	   entity field (symbol-value field) 'as-is)))
      entity)))

(luna-define-method elmo-msgdb-message-entity-number
  ((handler modb-legacy-entity-handler) entity)
  (and entity (aref (cdr entity) 0)))

(luna-define-method elmo-msgdb-message-entity-set-number
  ((handler modb-legacy-entity-handler) entity number)
  (and entity (aset (cdr entity) 0 number)))

(luna-define-method elmo-msgdb-message-entity-field
  ((handler modb-legacy-entity-handler) entity field &optional type)
  (and entity
       (let (index)
	 (modb-convert-field-value
	  modb-legacy-entity-specializer
	  field
	  (cond ((memq field '(message-id :message-id))
		 (car entity))
		((setq index (modb-legacy-entity-field-index field))
		 (aref (cdr entity) index))
		(t
		 (setq index (modb-legacy-entity-field-index :extra))
		 (cdr (assoc (symbol-name field)
			     (aref (cdr entity) index)))))
	  type))))

(luna-define-method elmo-msgdb-message-entity-set-field
  ((handler modb-legacy-entity-handler) entity field value)
  (modb-legacy-entity-set-field entity field value))

(luna-define-method elmo-msgdb-copy-message-entity
  ((handler modb-legacy-entity-handler) entity &optional make-handler)
  (if make-handler
      (let ((copy (elmo-msgdb-make-message-entity make-handler)))
	(dolist (field (append '(message-id number references from subject
					    date to cc size)
			       (mapcar (lambda (extra) (intern (car extra)))
				       (aref (cdr entity) 8))))
	  (elmo-msgdb-message-entity-set-field
	   make-handler copy field
	   (elmo-msgdb-message-entity-field handler entity field)))
	copy)
    (cons (car entity)
	  (copy-sequence (cdr entity)))))

(luna-define-method elmo-msgdb-message-match-condition
  ((handler modb-entity-handler) condition entity)
  (let ((key (elmo-filter-key condition))
	(case-fold-search t)
	field-value)
    (cond
     ((or (string= key "since")
	  (string= key "before"))
      (let ((field-date (elmo-msgdb-message-entity-field
			 handler entity 'date))
	    (specified-date
	     (elmo-datevec-to-time
	      (elmo-date-get-datevec
	       (elmo-filter-value condition)))))
	(if (string= key "since")
	    (not (elmo-time< field-date specified-date))
	  (elmo-time< field-date specified-date))))
     ((or (string= key "larger")
	  (string= key "smaller"))
      (let ((bytes (elmo-msgdb-message-entity-field handler entity 'size))
	    (threshold (string-to-number (elmo-filter-value condition))))
	(if (string= key "larger")
	    (> bytes threshold)
	  (< bytes threshold))))
     ((setq field-value (elmo-msgdb-message-entity-field handler
							 entity
							 (intern key)
							 'string))
      (and (stringp field-value)
	   (string-match (elmo-filter-value condition) field-value)))
     (t
      condition))))


;; Standard implementation.
(eval-and-compile
  (luna-define-class modb-standard-entity-handler (modb-entity-handler)))

(defconst modb-standard-entity-field-slots
  '(number
    from
    subject
    date
    to
    cc
    content-type
    references
    size
    score
    extra))

(defconst modb-standard-entity-field-indices
  (modb-entity-create-field-indices modb-standard-entity-field-slots))

(defvar modb-standard-entity-normalizer nil)
(modb-set-field-converter 'modb-standard-entity-normalizer nil
  'message-id	nil
  'number	nil
  'date		#'modb-entity-parse-date-string
  'to		#'modb-entity-parse-address-string
  'cc		#'modb-entity-parse-address-string
  'references	nil
  'size		nil
  'score	nil
  t		#'modb-entity-encode-string-recursive)

(defvar modb-standard-entity-specializer nil)
(modb-set-field-converter 'modb-standard-entity-specializer nil
  'message-id	nil
  'number	nil
  'date		nil
  'references	nil
  'size		nil
  'score	nil
  t		#'modb-entity-decode-string-recursive)
(modb-set-field-converter 'modb-standard-entity-specializer 'string
  'message-id	nil
  'number	nil
  'date		#'modb-entity-make-date-string
  'to		#'modb-entity-make-address-string
  'cc		#'modb-entity-make-address-string
  'references	nil
  'size		nil
  'score	nil
  'ml-info	#'modb-entity-make-mailing-list-info-string
  t		#'modb-entity-decode-string-recursive)

(defmacro modb-standard-entity-field-index (field)
  `(cdr (assq ,field modb-standard-entity-field-indices)))

(defsubst modb-standard-entity-set-field (entity field value &optional as-is)
  (when entity
    (let (index)
      (unless as-is
	(let ((elmo-mime-charset
	       (modb-entity-handler-mime-charset (car entity))))
	  (setq value (modb-convert-field-value modb-standard-entity-normalizer
						field value))))
      (cond ((memq field '(message-id :message-id))
	     (setcar (cdr entity) value))
	    ((setq index (modb-standard-entity-field-index field))
	     (aset (cdr (cdr entity)) index value))
	    (t
	     (setq index (modb-standard-entity-field-index :extra))
	     (let ((extras (aref (cdr (cdr entity)) index))
		   cell)
	       (if (setq cell (assq field extras))
		   (setcdr cell value)
		 (aset (cdr (cdr entity))
		       index
		       (cons (cons field value) extras)))))))))

(defsubst modb-standard-make-message-entity (handler args)
  (let ((entity (cons handler
		      (cons nil
			    (make-vector
			     (length modb-standard-entity-field-slots)
			     nil))))
	field value)
    (while args
      (setq field (pop args)
	    value (pop args))
      (when value
	(modb-standard-entity-set-field entity field value)))
    entity))

(luna-define-method elmo-msgdb-make-message-entity
  ((handler modb-standard-entity-handler) args)
  (modb-standard-make-message-entity handler args))

(luna-define-method elmo-msgdb-message-entity-number
  ((handler modb-standard-entity-handler) entity)
  (and entity (aref (cdr (cdr entity)) 0)))

(luna-define-method elmo-msgdb-message-entity-set-number
  ((handler modb-standard-entity-handler) entity number)
  (and entity (aset (cdr (cdr entity)) 0 number)))

(luna-define-method elmo-msgdb-message-entity-field
  ((handler modb-standard-entity-handler) entity field &optional type)
  (and entity
       (let ((elmo-mime-charset
	      (modb-entity-handler-mime-charset handler))
	     index)
	 (modb-convert-field-value
	  modb-standard-entity-specializer
	  field
	  (cond ((memq field '(message-id :message-id))
		 (car (cdr entity)))
		((setq index (modb-standard-entity-field-index field))
		 (aref (cdr (cdr entity)) index))
		(t
		 (setq index (modb-standard-entity-field-index :extra))
		 (cdr (assq field (aref (cdr (cdr entity)) index)))))
	  type))))

(luna-define-method elmo-msgdb-message-entity-set-field
  ((handler modb-standard-entity-handler) entity field value)
  (modb-standard-entity-set-field entity field value))

(luna-define-method elmo-msgdb-copy-message-entity
  ((handler modb-standard-entity-handler) entity &optional make-handler)
  (if make-handler
      (let ((copy (elmo-msgdb-make-message-entity make-handler)))
	(dolist (field (nconc
			(delq 'extra
			      (copy-sequence modb-standard-entity-field-slots))
			(mapcar 'car
				(aref
				 (cdr (cdr entity))
				 (modb-standard-entity-field-index :extra)))
			'(message-id)))
	  (elmo-msgdb-message-entity-set-field
	   make-handler copy field
	   (elmo-msgdb-message-entity-field handler entity field)))
	copy)
    (cons handler
	  (cons (car (cdr entity))
		(copy-sequence (cdr (cdr entity)))))))

(luna-define-method elmo-msgdb-create-message-entity-from-header
  ((handler modb-standard-entity-handler) number args)
  (let (entity size field-name field-body extractor file-attrib)
    (save-excursion
      (set-buffer-multibyte default-enable-multibyte-characters)
      (setq entity
	    (modb-standard-make-message-entity
	     handler
	     (append
	      args
	      (list
	       :number
	       number
	       :message-id
	       (elmo-msgdb-get-message-id-from-header)
	       :references
	       (elmo-msgdb-get-references-from-header)
	       :from
	       (elmo-replace-in-string
		(or (elmo-decoded-fetch-field "from" 'summary)
		    elmo-no-from)
		"\t" " ")
	       :subject
	       (elmo-replace-in-string
		(or (elmo-decoded-fetch-field "subject" 'summary)
		    elmo-no-subject)
		"\t" " ")
	       :date
	       (or (elmo-decoded-fetch-field "date" 'summary)
		   (when buffer-file-name
		     (timezone-make-date-arpa-standard
		      (current-time-string
		       (nth 5 (or file-attrib
				  (setq file-attrib
					(file-attributes buffer-file-name)))))
		      (current-time-zone))))
	       :to
	       (mapconcat
		(lambda (field-body)
		  (mime-decode-field-body field-body "to" 'summary))
		(elmo-multiple-field-body "to") ",")
	       :cc
	       (mapconcat
		(lambda (field-body)
		  (mime-decode-field-body field-body "cc" 'summary))
		(elmo-multiple-field-body "cc") ",")
	       :content-type
	       (elmo-decoded-fetch-field "content-type" 'summary)
	       :size
	       (if (setq size (std11-fetch-field "content-length"))
		   (string-to-number size)
		 (or (plist-get args :size)
		     (when buffer-file-name
		       (nth 7 (or file-attrib
				  (setq file-attrib
					(file-attributes buffer-file-name)))))
		     0))))))
      (dolist (extra (cons "newsgroups"
			   (remove "newsgroups" elmo-msgdb-extra-fields)))
	(unless (memq (setq field-name (intern (downcase extra)))
		      '(number message-id references from subject
			       date to cc content-type size))
	  (setq extractor  (nth 1 (assq field-name
					modb-entity-field-extractor-alist))
		field-body (if extractor
			       (funcall extractor field-name)
			     (elmo-decoded-fetch-field extra 'summary)))
	  (when field-body
	    (modb-standard-entity-set-field entity field-name field-body))))
      entity)))


;; mailing list info handling
(defun modb-entity-extract-mailing-list-info (field)
  (let* ((getter (lambda (field)
		   (elmo-decoded-fetch-field (symbol-name field) 'summary)))
	 (name (elmo-find-list-match-value
		elmo-mailing-list-name-spec-list
		getter))
	 (count (elmo-find-list-match-value
		  elmo-mailing-list-count-spec-list
		  getter)))
    (when (or name count)
      (cons name (and count (string-to-number count))))))

(defun modb-entity-ml-info-real-fields (field)
  (elmo-uniq-list
   (mapcar (lambda (entry)
	     (symbol-name (if (consp entry) (car entry) entry)))
	   (append elmo-mailing-list-name-spec-list
		   elmo-mailing-list-count-spec-list))))

(defun modb-entity-make-mailing-list-info-string (field value)
  (when (car value)
    (format (if (cdr value) "(%s %05.0f)" "(%s)")
	    (elmo-msgdb-get-decoded-cache (car value))
	    (cdr value))))

;; message buffer handler
(eval-and-compile
  (luna-define-class modb-buffer-entity-handler (modb-entity-handler)))

(defvar modb-buffer-entity-specializer nil)
(modb-set-field-converter 'modb-buffer-entity-specializer nil
  'date	#'elmo-time-parse-date-string)

(luna-define-method elmo-msgdb-make-message-entity
  ((handler modb-buffer-entity-handler) args)
  (cons handler (cons (or (plist-get args :number)
			  (plist-get args 'number))
		      (or (plist-get args :buffer)
			  (plist-get args 'buffer)
			  (current-buffer)))))

(luna-define-method elmo-msgdb-message-entity-number
  ((handler modb-buffer-entity-handler) entity)
  (car (cdr entity)))

(luna-define-method elmo-msgdb-message-entity-set-number
  ((handler modb-buffer-entity-handler) entity number)
  (and entity (setcar (cdr entity) number)))

(luna-define-method elmo-msgdb-message-entity-field
  ((handler modb-buffer-entity-handler) entity field &optional type)
  (and entity
       (let ((elmo-mime-charset
	      (modb-entity-handler-mime-charset handler)))
	 (modb-convert-field-value
	  modb-buffer-entity-specializer
	  field
	  (if (memq field '(number :number))
	      (car (cdr entity))
	    (with-current-buffer (cdr (cdr entity))
	      (let ((extractor
		     (nth 1 (assq field modb-entity-field-extractor-alist))))
		(if extractor
		    (funcall extractor field)
		  (mapconcat
		   (lambda (field-body)
		     (mime-decode-field-body field-body (symbol-name field)
					     'summary))
		   (elmo-multiple-field-body (symbol-name field))
		   "\n")))))
	  type))))

(luna-define-method elmo-msgdb-message-match-condition :around
  ((handler modb-buffer-entity-handler) condition entity)
  (let ((key (elmo-filter-key condition))
	(case-fold-search t))
    (cond
     ((string= (elmo-filter-key condition) "body")
      (modb-entity-match-entity-body
       (regexp-quote (elmo-filter-value condition))
       (mime-parse-buffer (cdr (cdr entity)))))
     ((string= (elmo-filter-key condition) "raw-body")
      (with-current-buffer (cdr (cdr entity))
	(decode-coding-region (point-min) (point-max)
	 		      elmo-mime-display-as-is-coding-system)
	(goto-char (point-min))
	(and (re-search-forward "^$" nil t)	   ; goto body
	     (search-forward (elmo-filter-value condition) nil t))))
     (t
      (luna-call-next-method)))))

(defun modb-entity-match-entity-body (regexp mime-entity)
  (let ((content-type (mime-entity-content-type mime-entity))
	children result)
    (cond
     ((setq children (mime-entity-children mime-entity))
      (while children
	(when (modb-entity-match-entity-body regexp (car children))
	  (setq result t
		children nil))
	(setq children (cdr children)))
      result)
     ((eq (mime-content-type-primary-type content-type) 'text)
      (string-match regexp
		    (elmo-mime-charset-decode-string
		     (mime-entity-content mime-entity)
		     (or (mime-content-type-parameter content-type "charset")
			 default-mime-charset)
		     'CRLF)))
     (t nil))))

(require 'product)
(product-provide (provide 'modb-entity) (require 'elmo-version))

;;; modb-entity.el ends here