1 /* -*- Mode: C++-C -*-
2 *
3 * Copyright 1994 Christopher B. Liebman
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without fee,
7 * provided that the above copyright notice appear in all copies and that
8 * both that copyright notice and this permission notice appear in
9 * supporting documentation, and that the name Christopher B. Liebman not
10 * be used in advertising or publicity pertaining to distribution of this
11 * software without specific, written prior permission.
12 *
13 * THIS SOFTWARE IS PROVIDED `AS-IS'. CHRISTOPHER B. LIEBMAN, DISCLAIMS
14 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
15 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16 * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL CHRISTOPHER
17 * B. LIEBMAN, BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING SPECIAL,
18 * INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, OR
19 * PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
20 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author : Chris Liebman
24 * Created On : Tue Jan 11 14:11:30 1994
25 * Last Modified By: Chris Liebman
26 * Last Modified On: Mon Mar 7 17:49:59 1994
27 * Update Count : 142
28 * Status : Released
29 *
30 * HISTORY
31 * 6-Mar-1994 Chris Liebman
32 * Last Modified: Sun Mar 6 22:35:49 1994 #122 (Chris Liebman)
33 * Added customization for mail annotations.
34 *
35 * 13-Feb-1994 Chris Liebman
36 * Last Modified: Sat Feb 12 23:17:13 1994 #51 (Chris Liebman)
37 * Added new mail annotation.
38 *
39 * 2-Feb-1994 Chris Liebman
40 * Last Modified: Mon Jan 31 23:13:16 1994 #34 (Chris Liebman)
41 * Added annotation support. This is only hooked up for scripts
42 * currently.
43 *
44 * 31-Jan-1994 Chris Liebman
45 * Last Modified: Sat Jan 29 20:40:41 1994 #33 (Chris Liebman)
46 * Added new search support and a function to create an item that
47 * has no headers.
48 *
49 * 24-Jan-1994 Chris Liebman
50 * Last Modified: Sun Jan 23 14:04:47 1994 #28 (Chris Liebman)
51 * Moved all of the sound / image searching to other files.
52 *
53 * 20-Jan-1994 Chris Liebman
54 * Last Modified: Tue Jan 18 14:38:39 1994 #22 (Chris Liebman)
55 * Added new header parsing and bindings.
56 *
57 * 14-Jan-1994 Chris Liebman
58 * Last Modified: Fri Jan 14 10:42:41 1994 #7 (Chris Liebman)
59 * Added new MailItemIgnore() function.
60 * MailItemCreate() now ignores items for which MailItemIgnore()
61 * returns true.
62 * Search the status header with bindings.
63 *
64 * PURPOSE
65 * Routines to manage the list of mail items.
66 */
67
68 #ifndef lint
69 static char *RCSid = "$Id: mail_items.c,v 1.20 1994/03/08 15:05:57 liebman Exp $";
70 #endif
71
72 #include "faces.h"
73 #ifdef LOOKUP_HOSTNAME
74 #include <netdb.h>
75 #endif
76
77 /*
78 * The list of mail items from mail box.
79 */
80
81 MailItem *TheMailItems;
82 MailItem *TheMailItemsTail;
83
84 /*
85 *
86 */
87
88 typedef enum mail_annotation_types
89 {
90 MailAnnotateNone,
91 MailAnnotateCount,
92 MailAnnotateUser,
93 MailAnnotateHost,
94 MailAnnotateUserHost,
95 MailAnnotateHeader
96 } MailAnnotationType;
97
98 typedef struct mail_annotation
99 {
100 MailAnnotationType type;
101 char* header;
102 } MailAnnotation;
103
104 static MailAnnotation* mailAnnotations;
105 static int annotationCount;
106 static MailAnnotation* unknownMailAnnotations;
107 static int unknownAnnotationCount;
108
109 static void
MailItemAnnotate(item,annotations)110 MailItemAnnotate(item, annotations)
111 MailItem* item;
112 char** annotations;
113 {
114 int i;
115 char buffer[30];
116 MailHeader* header;
117 int count;
118 MailAnnotation* annos;
119 char* p;
120
121 if (item->unknown)
122 {
123 count = unknownAnnotationCount;
124 annos = unknownMailAnnotations;
125 #ifdef ITEM_DEBUG
126 fprintf(stderr, "MailItemAnnotate(): using unknown annotations.\n");
127 #endif
128 }
129 else
130 {
131 count = annotationCount;
132 annos = mailAnnotations;
133 #ifdef ITEM_DEBUG
134 fprintf(stderr, "MailItemAnnotate(): using known annotations.\n");
135 #endif
136 }
137
138 if (item->annotations != NULL)
139 {
140 for (i = 0; item->annotations[i] != NULL; ++i)
141 {
142 XtFree(item->annotations[i]);
143 }
144
145 XtFree((char*)item->annotations);
146 item->annotations = NULL;
147 }
148
149 if (item->face == NULL)
150 {
151 return;
152 }
153
154 if (annotations == NULL)
155 {
156 item->annotations = (char**)XtCalloc(count + 1, sizeof(char*));
157
158 for(i = 0; i < count; ++i)
159 {
160 if (item->use_label == (i+1))
161 {
162 item->annotations[i] = XtNewString(item->label);
163 }
164 else
165 {
166 switch(annos[i].type)
167 {
168 case MailAnnotateNone:
169 break;
170
171 case MailAnnotateCount:
172 sprintf(buffer, "%d", item->face->count);
173 item->annotations[i] = XtNewString(buffer);
174 break;
175
176 case MailAnnotateUser:
177 item->annotations[i] = XtNewString(item->user);
178 break;
179
180 case MailAnnotateHost:
181 item->annotations[i] = XtNewString(item->host);
182 break;
183
184 case MailAnnotateUserHost:
185 item->annotations[i] = XtMalloc(strlen(item->user) +
186 strlen(item->host) + 2);
187 sprintf(item->annotations[i], "%s@%s", item->user, item->host);
188 break;
189
190 case MailAnnotateHeader:
191 header = MailHeaderFind(annos[i].header, item->headers);
192 if ((header != NULL) && (header->value != NULL))
193 {
194 p = SkipChars(header->value, " \t");
195 item->annotations[i] = XtNewString(p);
196 }
197 break;
198
199 default:
200 fprintf(stderr, "Bad MailAnnotationType: %d\n",
201 annos[i].type);
202 break;
203 }
204 }
205
206 if (item->annotations[i] == NULL)
207 {
208 item->annotations[i] = XtMalloc(1);
209 *(item->annotations[i]) = '\0';
210 }
211
212 #ifdef ITEM_DEBUG
213 fprintf(stderr, "item->annotation[%d]: <%s>\n",
214 i, item->annotations[i]);
215 #endif
216 }
217 }
218 else
219 {
220 /*
221 * Count annotations.
222 */
223
224 for(i = 0; annotations[i] != NULL; ++i);
225
226 /*
227 * Copy annotations.
228 */
229
230 item->annotations = (char**)XtCalloc(i+1, sizeof(char*));
231
232 for(i = 0; annotations[i] != NULL; ++i)
233 {
234 item->annotations[i] = XtNewString(annotations[i]);
235 }
236
237 item->annotations[i] = NULL;
238 }
239 }
240
241 static void
MailItemFree(item)242 MailItemFree(item)
243 MailItem *item;
244 {
245 if (item->next != NULL)
246 {
247 item->next->prev = item->prev;
248 }
249
250 if (item->prev != NULL)
251 {
252 item->prev->next = item->next;
253 }
254
255 if (TheMailItems == item)
256 {
257 TheMailItems = item->next;
258 }
259
260 if (TheMailItemsTail == item)
261 {
262 TheMailItemsTail = item->prev;
263 }
264
265 XtFree(item->user);
266 XtFree(item->host);
267 XtFree(item->realhost);
268 XtFree(item->label);
269 item->user = NULL;
270 item->host = NULL;
271 item->realhost = NULL;
272 item->label = NULL;
273
274 /*
275 * Free mail headers.
276 */
277
278 MailHeaderListFree(item->headers);
279 item->headers = NULL;
280
281 /*
282 * Free annotations.
283 */
284
285 item->face = NULL;
286 MailItemAnnotate(item, NULL);
287
288 FaceImageFree(item->image);
289 item->image = NULL;
290
291 #ifdef SOUND
292 FaceSoundFree(item->sound);
293 #endif /* SOUND */
294 item->sound = NULL;
295
296 FaceCommandFree(item->command);
297 item->command = NULL;
298
299 XtFree((void *)item);
300 }
301
302 /*
303 * Use the ignoreMessage expression to see if this message should be
304 * ignored.
305 */
306
307 static int
MailItemIgnore(item)308 MailItemIgnore(item)
309 MailItem *item;
310 {
311 FaceBinding *binding;
312
313 binding = FaceBindingCheck(item->headers,
314 TheFacesResources.ignore_message_bindings);
315
316 if (binding != NULL)
317 {
318 return 1;
319 }
320
321 return(0);
322 }
323
324
325 void
MailBoxClear()326 MailBoxClear()
327 {
328 MailItem *item;
329
330 for (item = TheMailItems; item != NULL; item = item->next)
331 {
332 item->in_use = 0;
333 }
334
335 FaceClear();
336 }
337
338 void
MailBoxUnClear()339 MailBoxUnClear()
340 {
341 MailItem *item;
342
343 for (item = TheMailItems; item != NULL; item = item->next)
344 {
345 item->in_use = 1;
346 item->face = FaceDisplay(item);
347 }
348 }
349
350 void
MailBoxClean()351 MailBoxClean()
352 {
353 MailItem *item;
354 MailItem *next_item;
355
356 for (item = TheMailItems; item != NULL; item = next_item)
357 {
358 /*
359 * Save item->next here because if MailItemFree() is called, it will
360 * free item and we won't be able to access it after the call.
361 * Found by phkmalloc. Philippe Charnier (charnier@xp11.frmug.org)
362 * 11/96.
363 */
364 next_item = item->next;
365
366 if (item->in_use == 0)
367 {
368 MailItemFree(item);
369 }
370 }
371
372 FaceClean();
373 }
374
375 static void
MailItemLabel(item)376 MailItemLabel(item)
377 MailItem* item;
378 {
379 if (item->label == NULL)
380 {
381 item->label = XtMalloc(strlen(item->user) + strlen(item->host) + 2);
382 sprintf(item->label, "%s@%s", item->user, item->host);
383 item->use_label = 0;
384 }
385 }
386
387
388 /*
389 * Create a new mail item
390 */
391
392 void
MailItemCreate(headers)393 MailItemCreate(headers)
394 MailHeader* headers;
395 {
396 MailItem *item;
397 MailHeader *from;
398 #ifdef LOOKUP_HOSTNAME
399 struct hostent *host;
400 #endif
401
402 /*
403 * Have we already seen this item?
404 */
405
406 for (item = TheMailItems; item != NULL; item = item->next)
407 {
408 if (item->in_use)
409 {
410 continue;
411 }
412
413 if (MailHeaderListCompare(headers, item->headers))
414 {
415 /*
416 * Yep!
417 */
418
419 item->face = FaceDisplay(item);
420 item->in_use = 1;
421
422 /*
423 * Setup annotations.
424 */
425
426 MailItemAnnotate(item, NULL);
427
428 /*
429 * Don't need these headers!
430 */
431
432 MailHeaderListFree(headers);
433
434 return;
435 }
436 }
437
438 item = (MailItem *)XtCalloc(1, sizeof(MailItem));
439 item->headers = headers;
440 item->in_use = 1;
441
442 /*
443 * Now parse the from line into the user and host parts.
444 */
445
446 from = MailHeaderFind(TheFacesResources.from_field, headers);
447
448 if (from == NULL)
449 {
450 from = MailHeaderFind("From:", headers);
451 }
452
453 if (from == NULL)
454 {
455 from = MailHeaderFind("From ", headers);
456 }
457
458 /*
459 * If we found no from line then we ignore this message.
460 */
461
462 if (from == NULL)
463 {
464 MailItemFree(item);
465 return;
466 }
467
468 /*
469 * Parse the from address.
470 */
471
472 MailParseAddress(from->value, &(item->user), &(item->host));
473
474 #ifdef LOOKUP_HOSTNAME
475 /*
476 * Lookup the host via gethostbyname if asked.
477 */
478
479 if (TheFacesResources.lookup_hostname)
480 {
481 host = gethostbyname(item->host);
482
483 if (host != NULL)
484 {
485 if (strcmp(item->host, host->h_name) != 0)
486 {
487 item->realhost = XtNewString(host->h_name);
488 }
489 #ifdef LOOKUP_DEBUG
490 fprintf(stderr, "lookup: <%s> -> <%s>\n",
491 item->host, host->h_name);
492 #endif
493 }
494 #ifdef LOOKUP_DEBUG
495 else
496 {
497 fprintf(stderr, "lookup: <%s> -> <>\n", item->host);
498 }
499 #endif
500
501 }
502 #endif /* LOOKUP_HOSTNAME */
503
504 /*
505 * Now see if we should ignore this message.
506 */
507
508 if (MailItemIgnore(item))
509 {
510 /*
511 * Yup! Do not need this item.
512 */
513
514 MailItemFree(item);
515 return;
516 }
517
518 /*
519 * Locate any image and sound.
520 */
521
522 FaceImageFind(item);
523 #ifdef SOUND
524 FaceSoundFind(item);
525 #endif
526 FaceCommandFind(item);
527
528 /*
529 * Compute the label (this is what is used to compress images.)
530 */
531
532 MailItemLabel(item);
533
534 /*
535 * Add this new item to the tail of the list.
536 */
537
538 if (TheMailItems == NULL)
539 {
540 TheMailItems = TheMailItemsTail = item;
541 }
542 else
543 {
544 item->prev = TheMailItemsTail;
545 item->prev->next = item;
546 TheMailItemsTail = item;
547 }
548
549 item->face = FaceDisplay(item);
550
551 /*
552 * Setup annotations.
553 */
554
555 MailItemAnnotate(item, NULL);
556
557 #ifdef SOUND
558 FaceSoundPlay(item->sound);
559 #endif /* SOUND */
560
561 FaceCommandRun(item->command);
562 }
563
564 void
MailItemCreateNoHeaders(user,host,annotations)565 MailItemCreateNoHeaders(user, host, annotations)
566 char* user;
567 char* host;
568 char** annotations;
569 {
570 MailItem *item;
571
572 if (annotations != NULL)
573 {
574 if (*annotations == NULL || **annotations == '\0')
575 {
576 annotations = NULL;
577 }
578 }
579
580 if (user == NULL)
581 {
582 user = "unknown";
583 }
584
585 if (host == NULL)
586 {
587 host = "LOCAL";
588 }
589
590 /*
591 * Have we already seen this item?
592 */
593
594 for (item = TheMailItems; item != NULL; item = item->next)
595 {
596 if (item->in_use)
597 {
598 continue;
599 }
600
601 if (strcmp(user, item->user) == 0 &&
602 strcmp(host, item->host) == 0)
603 {
604 #ifdef ITEM_DEBUG
605 fprintf(stderr,"MailItemCreateNoHeaders: reusing: <%s>:<%s>\n",
606 user, host);
607 #endif
608 /*
609 * Yep!
610 */
611
612 item->face = FaceDisplay(item);
613
614 /*
615 * Annotations may have changed.
616 */
617
618 MailItemAnnotate(item, annotations);
619 item->in_use = 1;
620 return;
621 }
622 }
623
624 #ifdef ITEM_DEBUG
625 fprintf(stderr,"MailItemCreateNoHeaders: creating: <%s>:<%s>\n",
626 user, host);
627 #endif
628
629 item = (MailItem *)XtCalloc(1, sizeof(MailItem));
630 item->headers = NULL;
631 item->user = XtNewString(user);
632 item->host = XtNewString(host);
633 item->in_use = 1;
634
635 /*
636 * Compute the label (this is what is used to compress images.)
637 */
638
639 MailItemLabel(item);
640
641 /*
642 * Locate any image and sound.
643 */
644
645 FaceImageFind(item);
646 #ifdef SOUND
647 FaceSoundFind(item);
648 #endif
649 FaceCommandFind(item);
650
651 /*
652 * Add this new item to the tail of the list.
653 */
654
655 if (TheMailItems == NULL)
656 {
657 TheMailItems = TheMailItemsTail = item;
658 }
659 else
660 {
661 item->prev = TheMailItemsTail;
662 item->prev->next = item;
663 TheMailItemsTail = item;
664 }
665
666 item->face = FaceDisplay(item);
667
668 MailItemAnnotate(item, annotations);
669
670 #ifdef SOUND
671 FaceSoundPlay(item->sound);
672 #endif /* SOUND */
673
674 FaceCommandRun(item->command);
675 }
676
677 void
MailBoxEmpty()678 MailBoxEmpty()
679 {
680 /*
681 * There is no file, clear all counts and clean. This
682 * will cause all faces to be removed.
683 */
684
685 MailBoxClear();
686 MailBoxClean();
687 }
688
689 void
MailItemInit()690 MailItemInit()
691 {
692 int i;
693 XrmDatabase db;
694 char* appname = XtName(TheTopLevel);
695 char* fullname;
696 char* fullclass;
697 String type;
698 XrmValue value;
699
700 #if (XtSpecificationRelease > 4)
701 db = XtScreenDatabase(XtScreen(TheTopLevel));
702 #else
703 db = XtDatabase(XtDisplay(TheTopLevel));
704 #endif
705
706 fullname = XtMalloc(strlen(appname) + 50);
707 fullclass = XtMalloc(strlen(XFACES_CLASS) + 50);
708
709 annotationCount = TheFacesResources.annotation_count;
710 mailAnnotations = (MailAnnotation*) XtCalloc(annotationCount,
711 sizeof(MailAnnotation));
712
713 for(i = 0; i < annotationCount; ++i)
714 {
715 type = NULL;
716 mailAnnotations[i].type = MailAnnotateNone;
717 mailAnnotations[i].header = NULL;
718
719 sprintf(fullname, "%s.mail.annotation%d", appname, i+1);
720 sprintf(fullclass, "%s.Mail.Annotation", XFACES_CLASS);
721
722 #ifdef ITEM_DEBUG
723 fprintf(stderr, "looking for: %s/%s\n", fullname, fullclass);
724 #endif
725
726 if (XrmGetResource(db, fullname, fullclass, &type, &value) &&
727 (strcmp(type, XtRString) == 0))
728 {
729 #ifdef ITEM_DEBUG
730 fprintf(stderr, "found: <%s>\n", (char*)(value.addr));
731 #endif
732 if (strcmp(value.addr, "none") == 0)
733 {
734 mailAnnotations[i].type = MailAnnotateNone;
735 }
736 else if (strcmp(value.addr, "count") == 0)
737 {
738 mailAnnotations[i].type = MailAnnotateCount;
739 }
740 else if (strcmp(value.addr, "user") == 0)
741 {
742 mailAnnotations[i].type = MailAnnotateUser;
743 }
744 else if (strcmp(value.addr, "host") == 0)
745 {
746 mailAnnotations[i].type = MailAnnotateHost;
747 }
748 else if (strcmp(value.addr, "user@host") == 0)
749 {
750 mailAnnotations[i].type = MailAnnotateUserHost;
751 }
752 else if (strncmp(value.addr, "*", 1) == 0)
753 {
754 mailAnnotations[i].type = MailAnnotateHeader;
755 mailAnnotations[i].header = (char*)(value.addr) + 1;
756 }
757
758 #ifdef ITEM_DEBUG
759 switch (mailAnnotations[i].type)
760 {
761 case MailAnnotateNone:
762 fprintf(stderr,
763 "mailAnnotations[%d].type: MailAnnotateNone\n", i);
764 break;
765
766 case MailAnnotateCount:
767 fprintf(stderr,
768 "mailAnnotations[%d].type: MailAnnotateCount\n", i);
769 break;
770
771 case MailAnnotateUser:
772 fprintf(stderr,
773 "mailAnnotations[%d].type: MailAnnotateUser\n", i);
774 break;
775
776 case MailAnnotateHost:
777 fprintf(stderr,
778 "mailAnnotations[%d].type: MailAnnotateHost\n", i);
779 break;
780
781 case MailAnnotateUserHost:
782 fprintf(stderr,
783 "mailAnnotations[%d].type: MailAnnotateUserHost\n", i);
784 break;
785
786 case MailAnnotateHeader:
787 fprintf(stderr,
788 "mailAnnotations[%d].type: MailAnnotateHeader\n", i);
789 fprintf(stderr,
790 "mailAnnotations[%d].header: <%s>\n",
791 i, mailAnnotations[i].header);
792 break;
793 }
794 #endif
795 }
796 };
797
798 unknownAnnotationCount = TheFacesResources.unknown_annotation_count;
799 unknownMailAnnotations = (MailAnnotation*)XtCalloc(unknownAnnotationCount,
800 sizeof(MailAnnotation));
801
802 for(i = 0; i < unknownAnnotationCount; ++i)
803 {
804 type = NULL;
805 unknownMailAnnotations[i].type = MailAnnotateNone;
806 unknownMailAnnotations[i].header = NULL;
807
808 sprintf(fullname, "%s.mail.unknownAnnotation%d", appname, i+1);
809 sprintf(fullclass, "%s.Mail.Annotation", XFACES_CLASS);
810
811 #ifdef ITEM_DEBUG
812 fprintf(stderr, "looking for: %s/%s\n", fullname, fullclass);
813 #endif
814
815 if (XrmGetResource(db, fullname, fullclass, &type, &value) &&
816 (strcmp(type, XtRString) == 0))
817 {
818 #ifdef ITEM_DEBUG
819 fprintf(stderr, "found: <%s>\n", (char*)(value.addr));
820 #endif
821 if (strcmp(value.addr, "none") == 0)
822 {
823 unknownMailAnnotations[i].type = MailAnnotateNone;
824 }
825 else if (strcmp(value.addr, "count") == 0)
826 {
827 unknownMailAnnotations[i].type = MailAnnotateCount;
828 }
829 else if (strcmp(value.addr, "user") == 0)
830 {
831 unknownMailAnnotations[i].type = MailAnnotateUser;
832 }
833 else if (strcmp(value.addr, "host") == 0)
834 {
835 unknownMailAnnotations[i].type = MailAnnotateHost;
836 }
837 else if (strcmp(value.addr, "user@host") == 0)
838 {
839 unknownMailAnnotations[i].type = MailAnnotateUserHost;
840 }
841 else if (strncmp(value.addr, "*", 1) == 0)
842 {
843 unknownMailAnnotations[i].type = MailAnnotateHeader;
844 unknownMailAnnotations[i].header = (char*)(value.addr) + 1;
845 }
846
847 #ifdef ITEM_DEBUG
848 switch (unknownMailAnnotations[i].type)
849 {
850 case MailAnnotateNone:
851 fprintf(stderr,
852 "unknownMailAnnotations[%d].type: MailAnnotateNone\n", i);
853 break;
854
855 case MailAnnotateCount:
856 fprintf(stderr,
857 "unknownMailAnnotations[%d].type: MailAnnotateCount\n", i);
858 break;
859
860 case MailAnnotateUser:
861 fprintf(stderr,
862 "unknownMailAnnotations[%d].type: MailAnnotateUser\n", i);
863 break;
864
865 case MailAnnotateHost:
866 fprintf(stderr,
867 "unknownMailAnnotations[%d].type: MailAnnotateHost\n", i);
868 break;
869
870 case MailAnnotateUserHost:
871 fprintf(stderr,
872 "unknownMailAnnotations[%d].type: MailAnnotateUserHost\n", i);
873 break;
874
875 case MailAnnotateHeader:
876 fprintf(stderr,
877 "unknownMailAnnotations[%d].type: MailAnnotateHeader\n", i);
878 fprintf(stderr,
879 "unknownMailAnnotations[%d].header: <%s>\n",
880 i, unknownMailAnnotations[i].header);
881 break;
882 }
883 #endif
884 }
885 };
886
887 XtFree(fullname);
888 XtFree(fullclass);
889 }
890