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