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
folder_addmsg(struct msgs ** mpp,char * msgfile,int selected,int unseen,int preserve,int deleting,char * from_dir)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
92 /*
93 ** check if this is highest or lowest message
94 */
95 if (mp->nummsg == 0) {
96 mp->lowmsg = msgnum;
97 mp->hghmsg = msgnum;
98 } else {
99 if (msgnum < mp->lowmsg)
100 mp->lowmsg = msgnum;
101 if (msgnum > mp->hghmsg)
102 mp->hghmsg = msgnum;
103 }
104
105 /* increment message count */
106 mp->nummsg++;
107
108 nmsg = m_name(msgnum);
109 snprintf(newmsg, sizeof(newmsg), "%s/%s", mp->foldpath, nmsg);
110
111 /*
112 ** Now try to link message into folder. Then run the
113 ** external hook on the message if one was specified in
114 ** the context. Run the refile hook if we're moving the
115 ** message from one place to another. We have to construct
116 ** the from path name for this because it's not there.
117 ** Run the add hook if the message is getting copied or
118 ** linked somewhere else.
119 */
120 if (link(msgfile, newmsg) != -1) {
121
122 if (deleting) {
123 snprintf(oldmsg, sizeof (oldmsg), "%s/%s",
124 from_dir, msgfile);
125 ext_hook("ref-hook", oldmsg, newmsg);
126 } else
127 ext_hook("add-hook", newmsg, NULL);
128
129 return msgnum;
130 } else {
131 linkerr = errno;
132
133 /*
134 ** Check if the file in our desired location is
135 ** the same as the source file. If so, then just
136 ** leave it alone and return. Otherwise, we will
137 ** continue the main loop and try again at another
138 ** slot (hghmsg+1).
139 */
140 if (linkerr == EEXIST) {
141 if (stat(msgfile, &st2) == 0 && stat(newmsg, &st1) == 0
142 && st2.st_ino == st1.st_ino) {
143 return msgnum;
144 } else {
145 continue;
146 }
147 }
148
149 /*
150 ** If link failed because we are trying to link
151 ** across devices, then check if there is a message
152 ** already in the desired location. If so, then return
153 ** error, else just copy the message.
154 */
155 if (linkerr == EXDEV) {
156 if (stat(newmsg, &st1) == 0) {
157 advise(NULL, "message %s:%s already exists", mp->foldpath, newmsg);
158 return -1;
159 } else {
160 if ((infd = open(msgfile, O_RDONLY)) == -1) {
161 advise(msgfile, "unable to open message %s", msgfile);
162 return -1;
163 }
164 fstat(infd, &st1);
165 if ((outfd = creat(newmsg, (int) st1.st_mode & 0777)) == -1) {
166 advise(newmsg, "unable to create");
167 close(infd);
168 return -1;
169 }
170 cpydata(infd, outfd, msgfile, newmsg);
171 close(infd);
172 close(outfd);
173
174 if (deleting) {
175 snprintf(oldmsg, sizeof oldmsg,
176 "%s/%s",
177 from_dir,
178 msgfile);
179 ext_hook("ref-hook", oldmsg, newmsg);
180 } else
181 ext_hook("add-hook", newmsg, NULL);
182
183 return msgnum;
184 }
185 }
186
187 /*
188 ** Else, some other type of link error,
189 ** so just return error.
190 */
191 advise(newmsg, "error linking %s to", msgfile);
192 return -1;
193 }
194 }
195 }
196