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