wfut 0.2.4
A client side C++ implementation of WFUT (WorldForge Update Tool).
wfut.cpp
1// This file may be redistributed and modified only under the terms of
2// the GNU Lesser General Public License (See COPYING for details).
3// Copyright (C) 2005 - 2008 Simon Goodall
4
5#ifdef HAVE_CONFIG_H
6 #include "config.h"
7#endif
8
9#include <getopt.h>
10
11#include <dirent.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <time.h>
15
16#include <algorithm>
17#include <cstdio>
18
19#include <sigc++/bind.h>
20
21#include <libwfut/WFUT.h>
22#include <libwfut/Encoder.h>
23#include <libwfut/platform.h>
24
25using namespace WFUT;
26
27static const bool debug = true;
28
29// getopt long argument struct.
30static struct option long_options [] =
31{
32 { "update", 1, 0, 'u' },
33 { "system", 1, 0, 's' },
34 { "server", 1, 0, 'S' },
35 { "prefix", 1, 0, 'p' },
36 { "version", 0, 0, 'v' },
37 { "help", 0, 0, 'h' },
38
39};
40
41// getopt short argument struct. One char per command
42// follow by a : to indicate that an argument it required
43static char short_options [] = "u:s:p:vS:h";
44
45static void recordUpdate(const FileObject &fo, const std::string &tmpfile) {
46 FILE *fp = 0;
47 if (!os_exists(tmpfile)) {
48 // Write header
49 fp = fopen(tmpfile.c_str(), "wt");
50 if (!fp) {
51 //error
52 return;
53 }
54 fprintf(fp, "<?xml version=\"1.0\"?>\n");
55 fprintf(fp, "<fileList dir=\"\">\n");
56 } else {
57 fp = fopen(tmpfile.c_str(), "at");
58 if (!fp) {
59 //error
60 return;
61 }
62 }
63 fprintf(fp, "<file filename=\"%s\" version=\"%d\" crc32=\"%lu\" size=\"%ld\" execute=\"%s\"/>\n", Encoder::encodeString(fo.filename).c_str(), fo.version, fo.crc32, fo.size, (fo.execute) ? ("true") : ("false"));
64 fclose(fp);
65
66}
67
68namespace {
69// Signal handler called when a file is sicessfully downloaded
70// We use this to update the local file list with the updated details
71void onDownloadComplete(const std::string& u, const std::string& f, const ChannelFileList& updates, ChannelFileList* local, const std::string& tmpfile) {
72 printf("Downloaded: %s\n", f.c_str());
73
74 const WFUT::FileMap& ulist = updates.getFiles();
75 WFUT::FileMap::const_iterator I = ulist.find(f);
76 // The fileobject should exist, otherwise how did we get here?
77 assert (I != ulist.end());
78 // Add the updated file record to the local list.
79 // addFile will overwrite any existing fileobject with the same
80 // filename
81 local->addFile(I->second);
82
83 // We store in a tmp file the fact that we sucessfully downloaded
84 // this file, incase of a crash.
85 recordUpdate(I->second, tmpfile);
86}
87
88// Signal handler called when a download fails.
89void onDownloadFailed(const std::string& u, const std::string& f, const std::string& r, int* error) {
90 fprintf(stderr, "Error downloading: %s - %s\n", u.c_str(), r.c_str());
91 // Increment error count
92 ++error;
93}
94
95void onUpdateReason(const std::string& filename, const WFUT::WFUTUpdateReason wu) {
96 if (wu == WFUT::WFUT_UPDATE_MODIFIED) {
97 printf("%s has been modified, will not update.\n", filename.c_str());
98 }
99}
100
101void print_usage(const char* name) {
102 printf("WFUT Version: %s\n", VERSION);
103 printf("Usage: %s [options]\n", name);
104 printf("\nOptions:\n");
105 printf("\t-p, --prefix channel_name -- The destination directory. (Optional)\n");
106 printf("\t-s, --system channel_name -- The system channels directory. (Optional)\n");
107 printf("\t-S, --server channel_name -- The URL to the update server.(Optional)\n");
108 printf("\t-u, --update channel_name -- The name of the channel to update.\n");
109 printf("\t-v, --version -- Display the version information.\n");
110 printf("\t-h, --help -- Display this message.\n");
111}
112
113}
114int main(int argc, char *argv[]) {
115
116 // Set some default values which we can override with command line parameters.
117 std::string server_root = "http://white.worldforgedev.org/WFUT/";
118 std::string mirror_file = "mirrors.xml";
119 std::string channel_file = "wfut.xml";
120 std::string tmpfile = "tempwfut.xml";
121
122 std::string channel = ".";
123 std::string local_path = "./";
124 std::string system_path = "";
125
126 if (argc == 1) {
127 print_usage(argv[0]);
128 return 0;
129 }
130
131 while (true) {
132 int opt_index = 0;
133 int c = getopt_long(argc, argv, short_options, long_options, &opt_index);
134 if (c == -1) break;
135 switch (c) {
136 case '?':
137 case 'h':
138 print_usage(argv[0]);
139 return 0;
140 break;
141 case 'v':
142 fprintf(stderr, "WFUT Version: %s\n", VERSION);
143 return 0;
144 break;
145 case 'u':
146 if (optarg) {
147 channel = optarg;
148 } else {
149 fprintf(stderr, "Missing channel name\n");
150 }
151 break;
152 case 's':
153 if (optarg) {
154 system_path = optarg;
155 } else {
156 fprintf(stderr, "Missing system path\n");
157 }
158 break;
159 case 'p':
160 if (optarg) {
161 local_path = optarg;
162 } else {
163 fprintf(stderr, "Missing prefix\n");
164 }
165 break;
166 case 'S':
167 if (optarg) {
168 server_root = optarg;
169 } else {
170 fprintf(stderr, "Missing system path\n");
171 }
172 break;
173
174 default:
175 fprintf(stderr, "Unknown command: %c\n", c);
176 }
177 }
178
179 if (debug) printf("Channel name: %s\n", channel.c_str());
180 // This is the base path for all files that will be downloaded
181 const std::string &local_root = local_path + "/" + channel + "/";
182
183 WFUTClient wfut;
184 // Initialise wfut. This does the curl setup.
185 wfut.init();
186
187 // Get the list of mirrors
188 MirrorList mirrors;
189 const std::string mirror_url = server_root + "/" + mirror_file;
190 wfut.getMirrorList(mirror_url, mirrors);
191
192 // Randomly select a mirror to use.
193 if (mirrors.empty() == false) {
194 // Initialise random number generators. random_shuffle could use either
195 srand((unsigned)time(NULL));
196#if defined (WIN32) || defined (_WIN32) || defined( __WIN32__)
197#else
198 srand48((unsigned)time(NULL));
199#endif
200 // Shuffle mirror list
201 std::random_shuffle(mirrors.begin(), mirrors.end());
202 // Pick first mirror
203 server_root = (*mirrors.begin()).url;
204 }
205
206 // Define the channelfilelist objects we will use
207 ChannelFileList local, system, server, updates, tmplist;
208
209
210 // Look for local wfut file.
211 const std::string &local_wfut = local_path + "/" + channel + "/" + channel_file;
212 if (debug) printf("Local wfut: %s\n", local_wfut.c_str());
213
214 if (os_exists(local_wfut)) {
215 if (wfut.getLocalList(local_wfut, local)) {
216 fprintf(stderr, "Error reading local wfut.xml file\n");
217 wfut.shutdown();
218 return 1;
219 } else {
220 if (channel == ".") channel = local.getName();
221 }
222 }
223
224 // Look for tmpwfut file. If it exists, update the local files list.
225 const std::string &tmp_wfut = local_path + "/" + tmpfile;
226 if (debug) printf("Tmp wfut: %s\n", tmp_wfut.c_str());
227
228 if (os_exists(tmp_wfut)) {
229 if (wfut.getLocalList(tmp_wfut, tmplist)) {
230 fprintf(stderr, "Error reading tmpwfut.xml file\n");
231 wfut.shutdown();
232 return 1;
233 } else {
234 const FileMap &fm = tmplist.getFiles();
235 FileMap::const_iterator I = fm.begin();
236 FileMap::const_iterator Iend = fm.end();
237 for (; I != Iend; ++I) {
238 local.addFile(I->second);
239 }
240 }
241 }
242
243 // Look for a system wfut file
244 if (!system_path.empty()) {
245 const std::string &system_wfut = system_path + "/" + channel + "/" + channel_file;
246 if (debug) printf("System wfut: %s\n", system_wfut.c_str());
247
248 if (os_exists(system_wfut)) {
249 if (wfut.getLocalList(system_wfut, system)) {
250 fprintf(stderr, "Error reading system wfut.xml file\n");
251 wfut.shutdown();
252 return 1;
253 } else {
254 if (channel == ".") channel = system.getName();
255 }
256 }
257 }
258 // By now we should have a proper channel name. If not, then there is nothing
259 // we can do to find the server updates.
260 if (channel.empty() || channel == ".") {
261 fprintf(stderr, "Unable to determine channel name.\n");
262 wfut.shutdown();
263 return 1;
264 }
265
266 const std::string &server_wfut = server_root + "/" + channel + "/" + channel_file;
267 if (debug) printf("Server wfut: %s\n", server_wfut.c_str());
268
269 if (wfut.getFileList(server_wfut, server)) {
270 printf("Error downloading server channel file\n");
271 wfut.shutdown();
272 return 1;
273 }
274
275 if (debug) printf("Local Root: %s\n", local_root.c_str());
276
277 printf("Updating Channel: %s\n", channel.c_str());
278
279 // Now we have loaded all our data files, lets find out what we really need
280 // to download
281 wfut.UpdateReason.connect(sigc::ptr_fun(onUpdateReason));
282 if (wfut.calculateUpdates(server, system, local, updates, local_root)) {
283 printf("Error determining file to update\n");
284 wfut.shutdown();
285 return 1;
286 }
287
288 // Make sure the local file has the correct channel name
289 local.setName(server.getName());
290
291 // Queue the list of files to download
292 wfut.updateChannel(updates, server_root + "/" + channel, local_root);
293
294 // error counts the number of download failures. This is incremented in the
295 // download failed signal handler
296 int error = 0;
297 // Connect up the signal handlers
298 wfut.DownloadComplete.connect(sigc::bind(sigc::ptr_fun(onDownloadComplete), updates, &local, tmp_wfut));
299 wfut.DownloadFailed.connect(sigc::bind(sigc::ptr_fun(onDownloadFailed), &error));
300
301 // Keep polling to download some more file bits.
302 while (wfut.poll());
303
304 // Save the completed download list
305 wfut.saveLocalList(local, local_wfut);
306 // Delete tmpwfut.xml if we get here. We only keep it around in case of
307 // an abnormal exit and the real wfut.xml has not been saved.
308 if (os_exists(tmp_wfut)) remove(tmp_wfut.c_str());
309
310 // Clean up WFUT. Closes curl handles
311 wfut.shutdown();
312
313 if (error) {
314 fprintf(stderr, "%d files failed to download.\n", error);
315 }
316
317
318 // Return error. Will be 0 on success
319 return error;
320}
std::string getName() const
void addFile(const FileObject &fo)
const FileMap & getFiles() const
void setName(const std::string &name)
WFUTError getLocalList(const std::string &filename, ChannelFileList &files)
Definition: WFUT.cpp:192
sigc::signal< void, const std::string &, const std::string & > DownloadComplete
Definition: WFUT.h:149
WFUTError init()
Definition: WFUT.cpp:23
WFUTError getFileList(const std::string &url, ChannelFileList &files)
Definition: WFUT.cpp:154
WFUTError shutdown()
Definition: WFUT.cpp:41
sigc::signal< void, const std::string &, const std::string &, const std::string & > DownloadFailed
Definition: WFUT.h:158
WFUTError getMirrorList(const std::string &url, MirrorList &mirrors)
Definition: WFUT.cpp:81
WFUTError saveLocalList(const ChannelFileList &files, const std::string &filename)
Definition: WFUT.cpp:201
sigc::signal< void, const std::string &, const WFUTUpdateReason > UpdateReason
Definition: WFUT.h:166
WFUTError calculateUpdates(const ChannelFileList &server, const ChannelFileList &system, const ChannelFileList &local, ChannelFileList &updates, const std::string &prefix)
Definition: WFUT.cpp:215
void updateChannel(const ChannelFileList &updates, const std::string &urlPrefix, const std::string &pathPrefix)
Definition: WFUT.cpp:53