BPicture: Fix archive constructor.
[haiku.git] / src / kits / mail / numailkit.cpp
blobcb058d644e7cdf851b9d099cefc8f2f7e53c1672
1 /* Numail Kit - general header for using the kit
2 **
3 ** Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
4 */
7 #include <MailPrivate.h>
9 #include <stdio.h>
11 #include <Autolock.h>
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <File.h>
15 #include <FindDirectory.h>
16 #include <Locker.h>
17 #include <Messenger.h>
18 #include <Path.h>
19 #include <String.h>
22 #define timeout 5e5
25 namespace BPrivate {
28 BPath
29 default_mail_directory()
31 BPath path;
32 if (find_directory(B_USER_DIRECTORY, &path) == B_OK)
33 path.Append("mail");
34 else
35 path.SetTo("/boot/home/mail/");
37 return path;
41 BPath
42 default_mail_in_directory()
44 BPath path = default_mail_directory();
45 path.Append("in");
47 return path;
51 BPath
52 default_mail_out_directory()
54 BPath path = default_mail_directory();
55 path.Append("out");
57 return path;
61 status_t
62 WriteMessageFile(const BMessage& archive, const BPath& path, const char* name)
64 status_t ret = B_OK;
65 BString leaf = name;
66 leaf << ".tmp";
68 BEntry settings_entry;
69 BFile tmpfile;
70 bigtime_t now = system_time();
72 create_directory(path.Path(), 0777);
74 BDirectory account_dir(path.Path());
75 ret = account_dir.InitCheck();
76 if (ret != B_OK)
78 fprintf(stderr, "Couldn't open '%s': %s\n",
79 path.Path(), strerror(ret));
80 return ret;
83 // get an entry for the tempfile
84 // Get it here so that failure doesn't create any problems
85 ret = settings_entry.SetTo(&account_dir,leaf.String());
86 if (ret != B_OK)
88 fprintf(stderr, "Couldn't create an entry for '%s/%s': %s\n",
89 path.Path(), leaf.String(), strerror(ret));
90 return ret;
95 // Save to a temporary file
98 // Our goal is to write to a tempfile and then use 'rename' to
99 // link that file into place once it contains valid contents.
100 // Given the filesystem's guarantee of atomic "rename" oper-
101 // ations this will guarantee that any non-temp files in the
102 // config directory are valid configuration files.
104 // Ideally, we would be able to do the following:
105 // BFile tmpfile(&account_dir, "tmpfile", B_WRITE_ONLY|B_CREATE_FILE);
106 // // ...
107 // tmpfile.Relink(&account_dir,"realfile");
108 // But this doesn't work because there is no way in the API
109 // to link based on file descriptor. (There should be, for
110 // exactly this reason, and I see no reason that it can not
111 // be added to the API, but as it is not present now so we'll
112 // have to deal.) It has to be file-descriptor based because
113 // (a) all a BFile knows is its node/FD and (b) the file may
114 // be unlinked at any time, at which point renaming the entry
115 // to clobber the "realfile" will result in an invalid con-
116 // figuration file being created.
118 // We can't count on not clobbering the tempfile to gain
119 // exclusivity because, if the system crashes between when
120 // we create the tempfile an when we rename it, we will have
121 // a zombie tempfile that will prevent us from doing any more
122 // saves.
124 // What we can do is:
126 // Create or open the tempfile
127 // // At this point no one will *clobber* the file, but
128 // // others may open it
129 // Lock the tempfile
130 // // At this point, no one else may open it and we have
131 // // exclusive access to it. Because of the above, we
132 // // know that our entry is still valid
134 // Truncate the tempfile
135 // Write settings
136 // Sync
137 // Rename to the realfile
138 // // this does not affect the lock, but now open-
139 // // ing the realfile will fail with B_BUSY
140 // Unlock
142 // If this code is the only code that changes these files,
143 // then we are guaranteed that all realfiles will be valid
144 // settings files. I think that's the best we can do unless
145 // we get the Relink() api. An implementation of the above
146 // follows.
149 // Create or open
150 ret = B_TIMED_OUT;
151 while (system_time() - now < timeout) //-ATT-no timeout arg. Setting by #define
153 ret = tmpfile.SetTo(&settings_entry, B_WRITE_ONLY | B_CREATE_FILE);
154 if (ret != B_BUSY) break;
156 // wait 1/100th second
157 snooze((bigtime_t)1e4);
159 if (ret != B_OK)
161 fprintf(stderr, "Couldn't open '%s/%s' within the timeout period (%fs): %s\n",
162 path.Path(), leaf.String(), (float)timeout/1e6, strerror(ret));
163 return ret==B_BUSY? B_TIMED_OUT:ret;
166 // lock
167 ret = B_TIMED_OUT;
168 while (system_time() - now < timeout)
170 ret = tmpfile.Lock(); //-ATT-changed account_file to tmpfile. Is that allowed?
171 if (ret != B_BUSY) break;
173 // wait 1/100th second
174 snooze((bigtime_t)1e4);
176 if (ret != B_OK)
178 fprintf(stderr, "Couldn't lock '%s/%s' in within the timeout period (%fs): %s\n",
179 path.Path(), leaf.String(), (float)timeout/1e6, strerror(ret));
180 // Can't remove it here, since it might be someone else's.
181 // Leaving a zombie shouldn't cause any problems tho so
182 // that's OK.
183 return ret==B_BUSY? B_TIMED_OUT:ret;
186 // truncate
187 tmpfile.SetSize(0);
189 // write
190 ret = archive.Flatten(&tmpfile);
191 if (ret != B_OK)
193 fprintf(stderr, "Couldn't flatten settings to '%s/%s': %s\n",
194 path.Path(), leaf.String(), strerror(ret));
195 return ret;
198 // ensure it's actually writen
199 ret = tmpfile.Sync();
200 if (ret != B_OK)
202 fprintf(stderr, "Couldn't sync settings to '%s/%s': %s\n",
203 path.Path(), leaf.String(), strerror(ret));
204 return ret;
207 // clobber old settings
208 ret = settings_entry.Rename(name,true);
209 if (ret != B_OK)
211 fprintf(stderr, "Couldn't clobber old settings '%s/%s': %s\n",
212 path.Path(), name, strerror(ret));
213 return ret;
216 return B_OK;
220 } // namespace BPrivate