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