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