1 /* 2 * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "App.h" 8 #include "ServerSettings.h" 9 10 #include <stdio.h> 11 12 #include <Alert.h> 13 #include <Catalog.h> 14 #include <Entry.h> 15 #include <Message.h> 16 #include <package/PackageInfo.h> 17 #include <Path.h> 18 #include <Roster.h> 19 #include <Screen.h> 20 #include <String.h> 21 22 #include "support.h" 23 24 #include "FeaturedPackagesView.h" 25 #include "MainWindow.h" 26 27 28 #undef B_TRANSLATION_CONTEXT 29 #define B_TRANSLATION_CONTEXT "App" 30 31 32 App::App() 33 : 34 BApplication("application/x-vnd.Haiku-HaikuDepot"), 35 fMainWindow(NULL), 36 fWindowCount(0), 37 fSettingsRead(false) 38 { 39 _CheckPackageDaemonRuns(); 40 } 41 42 43 App::~App() 44 { 45 // We cannot let global destructors cleanup static BitmapRef objects, 46 // since calling BBitmap destructors needs a valid BApplication still 47 // around. That's why we do it here. 48 PackageInfo::CleanupDefaultIcon(); 49 FeaturedPackagesView::CleanupIcons(); 50 } 51 52 53 bool 54 App::QuitRequested() 55 { 56 if (fMainWindow != NULL 57 && fMainWindow->LockLooperWithTimeout(1500000) == B_OK) { 58 BMessage windowSettings; 59 fMainWindow->StoreSettings(windowSettings); 60 61 fMainWindow->UnlockLooper(); 62 63 _StoreSettings(windowSettings); 64 } 65 66 return true; 67 } 68 69 70 void 71 App::ReadyToRun() 72 { 73 if (fWindowCount > 0) 74 return; 75 76 BMessage settings; 77 _LoadSettings(settings); 78 79 fMainWindow = new MainWindow(settings); 80 _ShowWindow(fMainWindow); 81 } 82 83 84 void 85 App::MessageReceived(BMessage* message) 86 { 87 switch (message->what) { 88 case MSG_MAIN_WINDOW_CLOSED: 89 { 90 BMessage windowSettings; 91 if (message->FindMessage("window settings", 92 &windowSettings) == B_OK) { 93 _StoreSettings(windowSettings); 94 } 95 96 fWindowCount--; 97 if (fWindowCount == 0) 98 Quit(); 99 break; 100 } 101 102 default: 103 BApplication::MessageReceived(message); 104 break; 105 } 106 } 107 108 109 void 110 App::RefsReceived(BMessage* message) 111 { 112 entry_ref ref; 113 int32 index = 0; 114 while (message->FindRef("refs", index++, &ref) == B_OK) { 115 BEntry entry(&ref, true); 116 _Open(entry); 117 } 118 } 119 120 121 enum arg_switch { 122 UNKNOWN_SWITCH, 123 NOT_SWITCH, 124 HELP_SWITCH, 125 WEB_APP_BASE_URL_SWITCH, 126 URL_CONNECTION_TRACE_LOGGING_SWITCH, 127 }; 128 129 130 static void 131 app_print_help() 132 { 133 fprintf(stdout, "HaikuDepot "); 134 fprintf(stdout, "[-u|--webappbaseurl <web-app-base-url>] "); 135 fprintf(stdout, "[-h] "); 136 fprintf(stdout, "[-t|--urlconnectiontracelogging]\n"); 137 } 138 139 140 static arg_switch 141 app_resolve_switch(char *arg) 142 { 143 int arglen = strlen(arg); 144 145 if (arglen > 0 && arg[0] == '-') { 146 147 if (arglen > 3 && arg[1] == '-') { // long form 148 if (0 == strcmp(&arg[2], "webappbaseurl")) 149 return WEB_APP_BASE_URL_SWITCH; 150 151 if (0 == strcmp(&arg[2], "help")) 152 return HELP_SWITCH; 153 154 if (0 == strcmp(&arg[2], "urlconnectiontracelogging")) 155 return URL_CONNECTION_TRACE_LOGGING_SWITCH; 156 } else { 157 if (arglen == 2) { // short form 158 switch (arg[1]) { 159 case 'u': 160 return WEB_APP_BASE_URL_SWITCH; 161 162 case 'h': 163 return HELP_SWITCH; 164 165 case 't': 166 return URL_CONNECTION_TRACE_LOGGING_SWITCH; 167 } 168 } 169 } 170 171 return UNKNOWN_SWITCH; 172 } 173 174 return NOT_SWITCH; 175 } 176 177 178 void 179 App::ArgvReceived(int32 argc, char* argv[]) 180 { 181 for (int i = 1; i < argc;) { 182 switch (app_resolve_switch(argv[i])) { 183 184 case URL_CONNECTION_TRACE_LOGGING_SWITCH: 185 ServerSettings::EnableUrlConnectionTraceLogging(); 186 break; 187 188 case HELP_SWITCH: 189 app_print_help(); 190 Quit(); 191 break; 192 193 case WEB_APP_BASE_URL_SWITCH: 194 if (i == argc-1) { 195 fprintf(stdout, "unexpected end of arguments; missing " 196 "web-app base url\n"); 197 Quit(); 198 } 199 200 if (ServerSettings::SetBaseUrl(BUrl(argv[i + 1])) != B_OK) { 201 fprintf(stdout, "malformed web app base url; %s\n", 202 argv[i + 1]); 203 Quit(); 204 } 205 else { 206 fprintf(stdout, "did configure the web base url; %s\n", 207 argv[i + 1]); 208 } 209 210 i++; // also move past the url value 211 212 break; 213 214 case NOT_SWITCH: 215 { 216 BEntry entry(argv[i], true); 217 _Open(entry); 218 break; 219 } 220 221 case UNKNOWN_SWITCH: 222 fprintf(stdout, "unknown switch; %s\n", argv[i]); 223 Quit(); 224 break; 225 } 226 227 i++; // move on at least one arg 228 } 229 } 230 231 232 // #pragma mark - private 233 234 235 void 236 App::_Open(const BEntry& entry) 237 { 238 BPath path; 239 if (!entry.Exists() || entry.GetPath(&path) != B_OK) { 240 fprintf(stderr, "Package file not found: %s\n", path.Path()); 241 return; 242 } 243 244 // Try to parse package file via Package Kit 245 BPackageKit::BPackageInfo info; 246 status_t status = info.ReadFromPackageFile(path.Path()); 247 if (status != B_OK) { 248 fprintf(stderr, "Failed to parse package file: %s\n", 249 strerror(status)); 250 return; 251 } 252 253 // Transfer information into PackageInfo 254 PackageInfoRef package(new(std::nothrow) PackageInfo(info), true); 255 if (package.Get() == NULL) { 256 fprintf(stderr, "Could not allocate PackageInfo\n"); 257 return; 258 } 259 260 package->SetLocalFilePath(path.Path()); 261 262 BMessage settings; 263 _LoadSettings(settings); 264 265 MainWindow* window = new MainWindow(settings, package); 266 _ShowWindow(window); 267 } 268 269 270 void 271 App::_ShowWindow(MainWindow* window) 272 { 273 window->Show(); 274 fWindowCount++; 275 } 276 277 278 bool 279 App::_LoadSettings(BMessage& settings) 280 { 281 if (!fSettingsRead) { 282 fSettings = true; 283 if (load_settings(&fSettings, "main_settings", "HaikuDepot") != B_OK) 284 fSettings.MakeEmpty(); 285 } 286 settings = fSettings; 287 return !fSettings.IsEmpty(); 288 } 289 290 291 void 292 App::_StoreSettings(const BMessage& settings) 293 { 294 // Take what is in settings and replace data under the same name in 295 // fSettings, leaving anything in fSettings that is not contained in 296 // settings. 297 int32 i = 0; 298 299 char* name; 300 type_code type; 301 int32 count; 302 303 while (settings.GetInfo(B_ANY_TYPE, i++, &name, &type, &count) == B_OK) { 304 fSettings.RemoveName(name); 305 for (int32 j = 0; j < count; j++) { 306 const void* data; 307 ssize_t size; 308 if (settings.FindData(name, type, j, &data, &size) != B_OK) 309 break; 310 fSettings.AddData(name, type, data, size); 311 } 312 } 313 314 save_settings(&fSettings, "main_settings", "HaikuDepot"); 315 } 316 317 318 // #pragma mark - 319 320 321 static const char* kPackageDaemonSignature 322 = "application/x-vnd.haiku-package_daemon"; 323 324 void 325 App::_CheckPackageDaemonRuns() 326 { 327 while (!be_roster->IsRunning(kPackageDaemonSignature)) { 328 BAlert* alert = new BAlert("start_package_daemon", 329 B_TRANSLATE("HaikuDepot needs the package daemon to function, " 330 "and it appears to be not running.\n" 331 "Would you like to start it now?"), 332 B_TRANSLATE("No, quit HaikuDepot"), 333 B_TRANSLATE("Start package daemon"), NULL, B_WIDTH_AS_USUAL, 334 B_WARNING_ALERT); 335 alert->SetShortcut(0, B_ESCAPE); 336 337 if (alert->Go() == 0) 338 exit(1); 339 340 if (!_LaunchPackageDaemon()) 341 break; 342 } 343 } 344 345 346 bool 347 App::_LaunchPackageDaemon() 348 { 349 status_t ret = be_roster->Launch(kPackageDaemonSignature); 350 if (ret != B_OK) { 351 BString errorMessage 352 = B_TRANSLATE("Starting the package daemon failed:\n\n%Error%"); 353 errorMessage.ReplaceAll("%Error%", strerror(ret)); 354 355 BAlert* alert = new BAlert("package_daemon_problem", 356 errorMessage, 357 B_TRANSLATE("Quit HaikuDepot"), 358 B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL, 359 B_WARNING_ALERT); 360 alert->SetShortcut(0, B_ESCAPE); 361 362 if (alert->Go() == 0) 363 return false; 364 } 365 // TODO: Would be nice to send a message to the package daemon instead 366 // and get a reply once it is ready. 367 snooze(2000000); 368 return true; 369 } 370 371