2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / application.cpp
bloba6415090a925f0af1b0c6e92f5a4fa1ff3e80131
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * application.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <glib/gstdio.h>
17 #include <fcntl.h>
18 #include <errno.h>
20 #include "application.h"
21 #include "runtime.h"
22 #include "deployment.h"
23 #include "utils.h"
24 #include "uri.h"
27 Application::Application ()
29 SetObjectType (Type::APPLICATION);
31 resource_root = NULL;
33 apply_default_style_cb = NULL;
34 apply_style_cb = NULL;
35 convert_keyframe_callback = NULL;
36 get_resource_cb = NULL;
39 Application::~Application ()
41 if (resource_root) {
42 Deployment::GetCurrent()->UntrackPath (resource_root);
43 RemoveDir (resource_root);
44 g_free (resource_root);
48 Application*
49 Application::GetCurrent ()
51 return Deployment::GetCurrent()->GetCurrentApplication();
54 void
55 Application::SetCurrent (Application *application)
57 Deployment::GetCurrent()->SetCurrentApplication (application);
60 void
61 Application::RegisterCallbacks (ApplyDefaultStyleCallback apply_default_style_cb,
62 ApplyStyleCallback apply_style_cb,
63 GetResourceCallback get_resource_cb,
64 ConvertKeyframeValueCallback convert_keyframe_callback)
66 this->apply_default_style_cb = apply_default_style_cb;
67 this->apply_style_cb = apply_style_cb;
68 this->convert_keyframe_callback = convert_keyframe_callback;
69 this->get_resource_cb = get_resource_cb;
72 void
73 Application::ApplyDefaultStyle (FrameworkElement *fwe, ManagedTypeInfo *key)
75 if (apply_default_style_cb)
76 apply_default_style_cb (fwe, key);
79 void
80 Application::ApplyStyle (FrameworkElement *fwe, Style *style)
82 if (apply_style_cb)
83 apply_style_cb (fwe, style);
86 void
87 Application::ConvertKeyframeValue (Type::Kind kind, DependencyProperty *property, Value *original, Value *converted)
89 if (convert_keyframe_callback) {
90 convert_keyframe_callback (kind, property, original, converted);
91 } else {
92 converted = new Value (*original);
96 struct NotifyCtx {
97 gpointer user_data;
98 NotifyFunc notify_cb;
99 WriteFunc write_cb;
102 static void downloader_abort (gpointer data, void *ctx);
103 static void downloader_progress_changed (EventObject *sender, EventArgs *calldata, gpointer closure);
104 static void downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure);
105 static void downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure);
106 static void downloader_write (void *data, gint32 offset, gint32 n, void *closure);
107 static void downloader_notify_size (gint64 size, gpointer closure);
109 bool
110 Application::GetResource (const char *resourceBase, const Uri *uri,
111 NotifyFunc notify_cb, WriteFunc write_cb,
112 DownloaderAccessPolicy policy,
113 Cancellable *cancellable, gpointer user_data)
115 if (!uri) {
116 g_warning ("Passing a null uri to Application::GetResource");
117 return false;
120 if (get_resource_cb && uri && !uri->isAbsolute) {
121 char *url = uri->ToString ();
122 ManagedStreamCallbacks stream = get_resource_cb (resourceBase, url);
123 g_free (url);
125 if (stream.handle) {
126 if (notify_cb) {
127 notify_cb (NotifyStarted, NULL, user_data);
128 notify_cb (NotifySize, stream.Length (stream.handle), user_data);
131 if (write_cb) {
132 char buf[4096];
133 int offset = 0;
134 int nread;
136 if (stream.CanSeek (stream.handle))
137 stream.Seek (stream.handle, 0, 0);
139 do {
140 if ((nread = stream.Read (stream.handle, buf, 0, sizeof (buf))) <= 0)
141 break;
143 write_cb (buf, offset, nread, user_data);
144 offset += nread;
145 } while (true);
148 if (notify_cb)
149 notify_cb (NotifyCompleted, NULL, user_data);
151 stream.Close (stream.handle);
153 return true;
157 #if 0
158 // FIXME: drt 171 and 173 expect this to fail simply because the uri
159 // begins with a '/', but other drts (like 238) depend on this
160 // working. I give up.
161 if (!uri->isAbsolute && uri->path && uri->path[0] == '/')
162 return false;
163 #endif
165 //no get_resource_cb or empty stream
166 Downloader *downloader;
167 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
168 if (!(downloader = surface->CreateDownloader ()))
169 return false;
171 NotifyCtx *ctx = g_new (NotifyCtx, 1);
172 ctx->user_data = user_data;
173 ctx->notify_cb = notify_cb;
174 ctx->write_cb = write_cb;
176 if (notify_cb) {
177 downloader->AddHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, ctx);
178 downloader->AddHandler (Downloader::DownloadFailedEvent, downloader_failed, ctx);
179 downloader->AddHandler (Downloader::CompletedEvent, downloader_complete, ctx);
182 if (cancellable) {
183 cancellable->SetCancelFuncAndData (downloader_abort, downloader, ctx);
186 if (downloader->Completed ()) {
187 if (notify_cb)
188 notify_cb (NotifyCompleted, NULL, user_data);
189 } else {
190 if (!downloader->Started ()) {
191 downloader->Open ("GET", (Uri*)uri, policy);
192 downloader->SetStreamFunctions (downloader_write, downloader_notify_size, ctx);
193 downloader->Send ();
197 return true;
200 static void
201 downloader_notify_size (gint64 size, gpointer closure)
203 NotifyCtx *ctx = (NotifyCtx *) closure;
204 ctx->notify_cb (NotifySize, size, ctx->user_data);
207 static void
208 downloader_write (void *data, gint32 offset, gint32 n, void *closure)
210 NotifyCtx *ctx = (NotifyCtx *) closure;
211 ctx->write_cb (data, offset, n, ctx->user_data);
214 static void
215 downloader_abort (gpointer data, void *ctx)
217 Downloader *dl = (Downloader *) data;
218 NotifyCtx *nc = (NotifyCtx *)ctx;
219 dl->RemoveHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, nc);
220 dl->RemoveHandler (Downloader::DownloadFailedEvent, downloader_failed, nc);
221 dl->RemoveHandler (Downloader::CompletedEvent, downloader_complete, nc);
222 dl->Abort ();
225 static void
226 downloader_progress_changed (EventObject *sender, EventArgs *calldata, gpointer closure)
228 NotifyCtx *ctx = (NotifyCtx *) closure;
229 Downloader *dl = (Downloader *) sender;
230 ctx->notify_cb (NotifyProgressChanged, (gint64) (100 * dl->GetDownloadProgress ()), ctx->user_data);
233 static void
234 downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
236 NotifyCtx *ctx = (NotifyCtx *) closure;
237 ctx->notify_cb (NotifyCompleted, NULL, ctx->user_data);
238 g_free (ctx);
239 ((Downloader *) sender)->unref_delayed ();
242 static void
243 downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
245 NotifyCtx *ctx = (NotifyCtx *) closure;
246 ctx->notify_cb (NotifyFailed, NULL, ctx->user_data);
247 g_free (ctx);
248 ((Downloader *) sender)->unref_delayed ();
251 //FIXME: nuke this!
252 char *
253 Application::GetResourceAsPath (const char *resourceBase, const Uri *uri)
255 char *dirname, *path, *filename, *url;
256 ManagedStreamCallbacks stream;
257 unzFile zipfile;
258 struct stat st;
259 char buf[4096];
260 int nread;
261 int fd;
263 if (!get_resource_cb || !uri || uri->isAbsolute)
264 return NULL;
266 // construct the path name for this resource
267 filename = uri->ToString ();
268 CanonicalizeFilename (filename, -1, CanonModeResource);
269 if (uri->GetQuery () != NULL) {
270 char *sc = strchr (filename, ';');
271 if (sc)
272 *sc = '/';
275 path = g_build_filename (GetResourceRoot(), filename, NULL);
276 g_free (filename);
278 if (g_stat (path, &st) != -1) {
279 // path exists, we're done
280 return path;
283 // create the directory for our resource (keeping the relative path intact)
284 dirname = g_path_get_dirname (path);
285 if (g_mkdir_with_parents (dirname, 0700) == -1 && errno != EEXIST) {
286 g_free (dirname);
287 g_free (path);
288 return NULL;
291 g_free (dirname);
293 url = uri->ToString ();
294 stream = get_resource_cb (resourceBase, url);
295 g_free (url);
297 if (!stream.handle) {
298 g_free (path);
299 return NULL;
302 // reset the stream to 0
303 if (stream.CanSeek (stream.handle))
304 stream.Seek (stream.handle, 0, SEEK_SET);
306 // create and save the buffer to disk
307 if ((fd = g_open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
308 stream.Close (stream.handle);
309 g_free (path);
310 return NULL;
313 // write the stream to disk
314 do {
315 if ((nread = stream.Read (stream.handle, buf, 0, sizeof (buf))) <= 0)
316 break;
318 if (write_all (fd, buf, (size_t) nread) == -1) {
319 stream.Close (stream.handle);
320 g_unlink (path);
321 g_free (path);
322 close (fd);
324 return NULL;
326 } while (true);
328 stream.Close (stream.handle);
329 close (fd);
331 // check to see if the resource is zipped
332 if (!(zipfile = unzOpen (path))) {
333 // nope, not zipped...
334 return path;
337 // create a directory to contain our unzipped content
338 if (!(dirname = CreateTempDir (path))) {
339 unzClose (zipfile);
340 g_free (dirname);
341 g_unlink (path);
342 g_free (path);
343 return NULL;
346 // unzip the contents
347 if (!ExtractAll (zipfile, dirname, CanonModeResource)) {
348 RemoveDir (dirname);
349 unzClose (zipfile);
350 g_free (dirname);
351 g_unlink (path);
352 g_free (path);
353 return NULL;
356 unzClose (zipfile);
357 g_unlink (path);
359 if (g_rename (dirname, path) == -1) {
360 RemoveDir (dirname);
361 g_free (dirname);
362 g_free (path);
363 return NULL;
366 g_free (dirname);
368 return path;
371 const char*
372 Application::GetResourceRoot ()
374 if (!resource_root) {
375 char *buf = g_build_filename (g_get_tmp_dir (), "moonlight-app.XXXXXX", NULL);
376 // create a root temp directory for all files
377 if (!(resource_root = MakeTempDir (buf)))
378 g_free (buf);
379 Deployment::GetCurrent()->TrackPath (resource_root);
381 return resource_root;