Added external program hooks.
[mmh] / sbr / folder_addmsg.c
1
2 /*
3  * folder_addmsg.c -- Link message into folder
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <fcntl.h>
14 #include <errno.h>
15
16 /*
17  * Link message into a folder.  Return the new number
18  * of the message.  If an error occurs, return -1.
19  */
20
21 int
22 folder_addmsg (struct msgs **mpp, char *msgfile, int selected,
23                int unseen, int preserve, int deleting)
24 {
25     int infd, outfd, linkerr, first_time, msgnum;
26     char *nmsg, newmsg[BUFSIZ];
27     char oldmsg[BUFSIZ];
28     struct msgs *op;
29     struct msgs *mp;
30     struct stat st1, st2;
31
32     first_time = 1;     /* this is first attempt */
33     mp = *mpp;
34
35     /*
36      * We might need to make several attempts
37      * in order to add the message to the folder.
38      */
39     for (;;) {
40         /*
41          * Get the message number we will attempt to add.
42          */
43         if (first_time) {
44             /* should we preserve the numbering of the message? */
45             if (preserve && (msgnum = m_atoi (msgfile)) > 0) {
46                 ;
47             } else if (mp->nummsg == 0) {
48                 /* check if we are adding to empty folder */
49                 msgnum = 1;
50             } else {
51                 /* else use highest message number + 1 */
52                 msgnum = mp->hghmsg + 1;
53             }
54             first_time = 0;
55         } else {
56             /* another attempt, so try next higher message number */
57             msgnum++;
58         }
59
60         /*
61          * See if we need more space.  If we need space at the
62          * end, then we allocate space for an addition 100 messages.
63          * If we need space at the beginning of the range, then just
64          * extend message status range to cover this message number.
65          */
66         if (msgnum > mp->hghoff) {
67             if ((mp = folder_realloc (mp, mp->lowoff, msgnum + 100)))
68                 *mpp = mp;
69             else {
70                 advise (NULL, "unable to allocate folder storage");
71                 return -1;
72             }
73         } else if (msgnum < mp->lowoff) {
74             if ((mp = folder_realloc (mp, msgnum, mp->hghoff)))
75                 *mpp = mp;
76             else {
77                 advise (NULL, "unable to allocate folder storage");
78                 return -1;
79             }
80         }
81
82         /*
83          * If a message is already in that slot,
84          * then loop to next available slot.
85          */
86         if (does_exist (mp, msgnum))
87             continue;
88
89         /* setup the bit flags for this message */
90         clear_msg_flags (mp, msgnum);
91         set_exists (mp, msgnum);
92
93         /* should we set the SELECT_UNSEEN bit? */
94         if (unseen) {
95             set_unseen (mp, msgnum);
96         }
97
98         /* should we set the SELECTED bit? */
99         if (selected) {
100             set_selected (mp, msgnum);
101
102             /* check if highest or lowest selected */
103             if (mp->numsel == 0) {
104                 mp->lowsel = msgnum;
105                 mp->hghsel = msgnum;
106             } else {
107                 if (msgnum < mp->lowsel)
108                     mp->lowsel = msgnum;
109                 if (msgnum > mp->hghsel)
110                     mp->hghsel = msgnum;
111             }
112
113             /* increment number selected */
114             mp->numsel++;
115         }
116
117         /*
118          * check if this is highest or lowest message
119          */
120         if (mp->nummsg == 0) {
121             mp->lowmsg = msgnum;
122             mp->hghmsg = msgnum;
123         } else {
124             if (msgnum < mp->lowmsg)
125                 mp->lowmsg = msgnum;
126             if (msgnum > mp->hghmsg)
127                 mp->hghmsg = msgnum;
128         }
129
130         /* increment message count */
131         mp->nummsg++;
132
133         nmsg = m_name (msgnum);
134         snprintf (newmsg, sizeof(newmsg), "%s/%s", mp->foldpath, nmsg);
135
136         /*
137          * Now try to link message into folder.
138          * Then run the external hook on the message if one was specified in the context.
139          * Run the refile hook if we're moving the message from one place to another.
140          * We have to construct the from path name for this because it's not there.
141          * Run the add hook if the message is getting copied or lined somewhere else.
142          */
143         if (link (msgfile, newmsg) != -1) {
144
145             if (deleting) {
146                 op = folder_read(getfolder(1));
147                 (void)snprintf(oldmsg, sizeof (oldmsg), "%s/%s", op->foldpath, msgfile);
148                 folder_free(op);
149                 (void)ext_hook("ref-hook", oldmsg, newmsg);
150             }
151             else
152                 (void)ext_hook("add-hook", newmsg, (char *)0);
153
154             return msgnum;
155         } else {
156             linkerr = errno;
157
158 #ifdef EISREMOTE
159             if (linkerr == EISREMOTE)
160                 linkerr = EXDEV;
161 #endif /* EISREMOTE */
162
163             /*
164              * Check if the file in our desired location is the same
165              * as the source file.  If so, then just leave it alone
166              * and return.  Otherwise, we will continue the main loop
167              * and try again at another slot (hghmsg+1).
168              */
169             if (linkerr == EEXIST) {
170                 if (stat (msgfile, &st2) == 0 && stat (newmsg, &st1) == 0
171                     && st2.st_ino == st1.st_ino) {
172                     return msgnum;
173                 } else {
174                     continue;
175                 }
176             }
177
178             /*
179              * If link failed because we are trying to link
180              * across devices, then check if there is a message
181              * already in the desired location.  If so, then return
182              * error, else just copy the message.
183              */
184             if (linkerr == EXDEV) {
185                 if (stat (newmsg, &st1) == 0) {
186                     advise (NULL, "message %s:%s already exists", newmsg);
187                     return -1;
188                 } else {
189                     if ((infd = open (msgfile, O_RDONLY)) == -1) {
190                         advise (msgfile, "unable to open message %s");
191                         return -1;
192                     }
193                     fstat (infd, &st1);
194                     if ((outfd = creat (newmsg, (int) st1.st_mode & 0777)) == -1) {
195                         advise (newmsg, "unable to create");
196                         close (infd);
197                         return -1;
198                     }
199                     cpydata (infd, outfd, msgfile, newmsg);
200                     close (infd);
201                     close (outfd);
202
203                     if (deleting)
204                         (void)ext_hook("ref-hook", newmsg, msgfile);
205                     else
206                         (void)ext_hook("add-hook", newmsg, (char *)0);
207
208                     return msgnum;
209                 }
210             }
211
212             /*
213              * Else, some other type of link error,
214              * so just return error.
215              */
216             advise (newmsg, "error linking %s to", msgfile);
217             return -1;
218         }
219     }
220 }