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