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