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