Author: korli Date: 2010-04-01 21:02:24 +0200 (Thu, 01 Apr 2010) New Revision: 36020 Changeset: http://dev.haiku-os.org/changeset/36020/haiku Ticket: http://dev.haiku-os.org/ticket/3762 Modified: haiku/trunk/src/apps/packageinstaller/Jamfile haiku/trunk/src/apps/packageinstaller/PackageInfo.cpp haiku/trunk/src/apps/packageinstaller/PackageInfo.h haiku/trunk/src/apps/packageinstaller/PackageItem.cpp haiku/trunk/src/apps/packageinstaller/PackageItem.h haiku/trunk/src/apps/packageinstaller/PackageStatus.cpp haiku/trunk/src/apps/packageinstaller/PackageStatus.h haiku/trunk/src/apps/packageinstaller/PackageView.cpp haiku/trunk/src/apps/packageinstaller/PackageView.h Log: Applied patch by sil2100 that enables handling of script files (bug #3762) Modified: haiku/trunk/src/apps/packageinstaller/Jamfile =================================================================== --- haiku/trunk/src/apps/packageinstaller/Jamfile 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/Jamfile 2010-04-01 19:02:24 UTC (rev 36020) @@ -11,6 +11,7 @@ PackageView.cpp PackageInfo.cpp PackageItem.cpp + PackageInstall.cpp PackageStatus.cpp PackageTextViewer.cpp PackageImageViewer.cpp Modified: haiku/trunk/src/apps/packageinstaller/PackageInfo.cpp =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageInfo.cpp 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageInfo.cpp 2010-04-01 19:02:24 UTC (rev 36020) @@ -39,7 +39,8 @@ P_NONE = 0, P_FILE, P_DIRECTORY, - P_LINK + P_LINK, + P_SCRIPT }; @@ -86,6 +87,14 @@ delete file; } + while (true) { + file = static_cast<PackageScript *>(fScripts.RemoveItem((long int)0)); + if (file == NULL) + break; + + delete file; + } + delete fPackageFile; } @@ -624,63 +633,31 @@ RETURN_AND_SET_STATUS(B_ERROR); } - // TODO: Here's the deal... there seems to be a strange ScrI tag that - // seems to mean script files (check this). It seems exaclty the same - // as a normal file (just as script files are normal files) so for - // now I'm treating those as files. Check if it's correct! - // No, it isn't and I will fix this soon. - if (!memcmp(buffer, "FilI", 5) || !memcmp(buffer, "ScrI", 5)) { - parser_debug("FilI\n"); - element = P_FILE; +#define INIT_VARS(tag, type) \ + parser_debug(tag "\n"); \ + element = type; \ + mimeString = ""; \ + nameString = ""; \ + linkString = ""; \ + signatureString = ""; \ + itemGroups = 0; \ + ctime = 0; \ + mtime = 0; \ + offset = 0; \ + cust = 0; \ + mode = 0; \ + platform = 0xffffffff; \ + size = 0; \ + originalSize = 0 - mimeString = ""; - nameString = ""; - signatureString = ""; - - itemGroups = 0; - ctime = 0; - mtime = 0; - offset = 0; - itemGroups = 0; - cust = 0; - mode = 0; - platform = 0xffffffff; - - size = 0; - originalSize = 0; + if (!memcmp(buffer, "FilI", 5)) { + INIT_VARS("FilI", P_FILE); } else if (!memcmp(buffer, "FldI", 5)) { - parser_debug("FldI\n"); - element = P_DIRECTORY; - - nameString = ""; - - itemGroups = 0; - ctime = 0; - mtime = 0; - offset = 0; - itemGroups = 0; - cust = 0; - platform = 0xffffffff; - - size = 0; - originalSize = 0; + INIT_VARS("FldI", P_DIRECTORY); } else if (!memcmp(buffer, "LnkI", 5)) { - parser_debug("LnkI\n"); - element = P_LINK; - - nameString = ""; - linkString = ""; - - itemGroups = 0; - ctime = 0; - mtime = 0; - offset = 0; - itemGroups = 0; - cust = 0; - platform = 0xffffffff; - - size = 0; - originalSize = 0; + INIT_VARS("LnkI", P_LINK); + } else if (!memcmp(buffer, "ScrI", 5)) { + INIT_VARS("ScrI", P_SCRIPT); } else if (!memcmp(buffer, "Name", 5)) { if (element == P_NONE) { RETURN_AND_SET_STATUS(B_ERROR); @@ -795,7 +772,7 @@ swap_data(B_UINT64_TYPE, &size, sizeof(uint64), B_SWAP_BENDIAN_TO_HOST); } else if (!memcmp(buffer, "OrgS", 5)) { - if (element != P_FILE && element != P_LINK) { + if (element != P_FILE && element != P_LINK && element != P_SCRIPT) { RETURN_AND_SET_STATUS(B_ERROR); } @@ -1019,6 +996,9 @@ item = new PackageLink(fPackageFile, dest, linkString, localType, ctime, mtime, mode, offset, size); } + } else if (element == P_SCRIPT) { + fScripts.AddItem(new PackageScript(fPackageFile, offset, size, + originalSize)); } else { // If the directory tree count is equal to zero, this means all // directory trees have been closed and a padding sequence means the Modified: haiku/trunk/src/apps/packageinstaller/PackageInfo.h =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageInfo.h 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageInfo.h 2010-04-01 19:02:24 UTC (rev 36020) @@ -32,7 +32,11 @@ const char *GetDisclaimer() { return fDisclaimer.String(); } BMallocIO *GetSplashScreen() { return fHasImage ? &fImage : NULL; } int32 GetProfileCount() { return fProfiles.CountItems(); } - pkg_profile *GetProfile(int32 num) { return static_cast<pkg_profile *>(fProfiles.ItemAt(num)); } + pkg_profile *GetProfile(int32 num) { + return static_cast<pkg_profile *>(fProfiles.ItemAt(num)); } + int32 GetScriptCount() { return fScripts.CountItems(); } + PackageScript *GetScript(int32 num) { + return static_cast<PackageScript *>(fScripts.ItemAt(num)); } status_t Parse(); status_t InitCheck() { return fStatus; } @@ -56,6 +60,7 @@ bool fHasImage; BList fFiles; // Holds all files in the package + BList fScripts; }; Modified: haiku/trunk/src/apps/packageinstaller/PackageItem.cpp =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageItem.cpp 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageItem.cpp 2010-04-01 19:02:24 UTC (rev 36020) @@ -16,6 +16,7 @@ #include <Directory.h> #include <fs_info.h> #include <NodeInfo.h> +#include <OS.h> #include <SymLink.h> #include <Volume.h> @@ -37,10 +38,14 @@ P_ATTRIBUTE }; +extern const char **environ; + status_t inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize) { + parser_debug("inflate_data() called - input_size: %ld, output_size: %ld\n", + inSize, outSize); z_stream stream; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; @@ -375,6 +380,68 @@ status_t +PackageItem::SkipAttribute(uint8 *buffer, bool *attrStarted, bool *done) +{ + status_t ret = B_OK; + uint32 length; + + if (!memcmp(buffer, "BeAI", 5)) { + parser_debug(" Attribute started.\n"); + *attrStarted = true; + } else if (!memcmp(buffer, "BeAN", 5)) { + if (!*attrStarted) { + ret = B_ERROR; + return ret; + } + + parser_debug(" BeAN.\n"); + fPackage->Read(&length, 4); + swap_data(B_UINT32_TYPE, &length, sizeof(uint32), + B_SWAP_BENDIAN_TO_HOST); + + fPackage->Seek(length, SEEK_CUR); + } else if (!memcmp(buffer, "BeAT", 5)) { + if (!*attrStarted) { + ret = B_ERROR; + return ret; + } + + parser_debug(" BeAT.\n"); + fPackage->Seek(4, SEEK_CUR); + } else if (!memcmp(buffer, "BeAD", 5)) { + if (!*attrStarted) { + ret = B_ERROR; + return ret; + } + + parser_debug(" BeAD.\n"); + uint64 length64; + fPackage->Read(&length64, 8); + swap_data(B_UINT64_TYPE, &length64, sizeof(length64), + B_SWAP_BENDIAN_TO_HOST); + + fPackage->Seek(12 + length64, SEEK_CUR); + + parser_debug(" Data skipped successfuly.\n"); + } else if (!memcmp(buffer, padding, 7)) { + if (!*attrStarted) { + *done = true; + return ret; + } + + parser_debug(" Padding.\n"); + *attrStarted = false; + parser_debug(" > Attribute skipped.\n"); + } else { + parser_debug(" Unknown attribute\n"); + ret = B_ERROR; + } + + return ret; +} + + +status_t PackageItem::ParseData(uint8 *buffer, BFile *file, uint64 originalSize, bool *done) { @@ -425,6 +492,203 @@ } +// #pragma mark - PackageScript + + +PackageScript::PackageScript(BFile *parent, uint64 offset, uint64 size, + uint64 originalSize) + : + PackageItem(parent, NULL, 0, 0, 0, offset, size), + fOriginalSize(originalSize), + fThreadId(-1) +{ +} + + +status_t +PackageScript::DoInstall(const char *path, ItemState *state) +{ + status_t ret = B_OK; + parser_debug("Script: DoInstall() called!\n"); + + if (fOffset) { + parser_debug("We have an offset\n"); + if (!fPackage) + return B_ERROR; + + ret = fPackage->InitCheck(); + if (ret != B_OK) + return ret; + + // We need to parse the data section now + fPackage->Seek(fOffset, SEEK_SET); + uint8 buffer[7]; + bool attrStarted = false, done = false; + + uint8 section = P_ATTRIBUTE; + + while (fPackage->Read(buffer, 7) == 7) { + if (!memcmp(buffer, "FBeA", 5)) { + parser_debug("-> Attribute\n"); + section = P_ATTRIBUTE; + continue; + } else if (!memcmp(buffer, "FiDa", 5)) { + parser_debug("-> File data\n"); + section = P_DATA; + continue; + } + + switch (section) { + case P_ATTRIBUTE: + ret = SkipAttribute(buffer, &attrStarted, &done); + break; + + case P_DATA: + ret = _ParseScript(buffer, fOriginalSize, &done); + break; + + default: + return B_ERROR; + } + + if (ret != B_OK || done) + break; + } + } + + parser_debug("Ret: %d %s\n", ret, strerror(ret)); + return ret; +} + + +const char* +PackageScript::ItemKind() +{ + return "script"; +} + + +status_t +PackageScript::_ParseScript(uint8 *buffer, uint64 originalSize, bool *done) +{ + status_t ret = B_OK; + + if (!memcmp(buffer, "FiMF", 5)) { + parser_debug(" Found file (script) data.\n"); + uint64 compressed, original; + fPackage->Read(&compressed, 8); + swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64), + B_SWAP_BENDIAN_TO_HOST); + + fPackage->Read(&original, 8); + swap_data(B_UINT64_TYPE, &original, sizeof(uint64), + B_SWAP_BENDIAN_TO_HOST); + parser_debug(" Still good... (%llu : %llu)\n", original, + originalSize); + + if (original != originalSize) { + parser_debug(" File size mismatch\n"); + return B_ERROR; // File size mismatch + } + parser_debug(" Still good...\n"); + + if (fPackage->Read(buffer, 4) != 4) { + parser_debug(" Read(buffer, 4) failed\n"); + return B_ERROR; + } + parser_debug(" Still good...\n"); + + uint8 *temp = new uint8[compressed]; + if (fPackage->Read(temp, compressed) != (int64)compressed) { + parser_debug(" Read(temp, compressed) failed\n"); + delete[] temp; + return B_ERROR; + } + + uint8 *script = new uint8[original]; + ret = inflate_data(temp, compressed, script, original); + if (ret != B_OK) { + parser_debug(" inflate_data failed\n"); + delete[] temp; + delete[] script; + return ret; + } + + ret = _RunScript(script, originalSize); + delete[] script; + delete[] temp; + parser_debug(" Script data inflation complete!\n"); + } else if (!memcmp(buffer, padding, 7)) { + *done = true; + return ret; + } else { + parser_debug("_ParseData unknown tag\n"); + ret = B_ERROR; + } + + return ret; +} + + +status_t +PackageScript::_RunScript(uint8 *script, uint32 len) +{ + // This function written by Peter Folk <pfolk@xxxxxxxxxxxx> + // and published in the BeDevTalk FAQ, modified for use in the + // PackageInstaller + // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 + + // Save current FDs + int old_in = dup(0); + int old_out = dup(1); + int old_err = dup(2); + + int filedes[2]; + + /* Create new pipe FDs as stdin, stdout, stderr */ + pipe(filedes); dup2(filedes[0], 0); close(filedes[0]); + int in = filedes[1]; // Write to in, appears on cmd's stdin + pipe(filedes); dup2(filedes[1], 1); close(filedes[1]); + pipe(filedes); dup2(filedes[1], 2); close(filedes[1]); + + const char **argv = new const char * [3]; + argv[0] = strdup("/bin/sh"); + argv[1] = strdup("-s"); + argv[2] = NULL; + + // "load" command. + fThreadId = load_image(2, argv, environ); + + int i; + for (i = 0; i < 2; i++) + delete argv[i]; + delete [] argv; + + if (fThreadId < B_OK) + return fThreadId; + + // thread id is now suspended. + setpgid(fThreadId, fThreadId); + + // Restore old FDs + close(0); dup(old_in); close(old_in); + close(1); dup(old_out); close(old_out); + close(2); dup(old_err); close(old_err); + + set_thread_priority(fThreadId, B_LOW_PRIORITY); + resume_thread(fThreadId); + + // Write the script + if (write(in, script, len) != (int32)len || write(in, "\nexit\n", 6) != 6) { + parser_debug("Writing script failed\n"); + kill_thread(fThreadId); + return B_ERROR; + } + + return B_OK; +} + + // #pragma mark - PackageDirectory @@ -437,11 +701,11 @@ status_t -PackageDirectory::WriteToPath(const char *path, ItemState *state) +PackageDirectory::DoInstall(const char *path, ItemState *state) { BPath &destination = state->destination; status_t ret; - parser_debug("Directory: %s WriteToPath() called!\n", fPath.String()); + parser_debug("Directory: %s DoInstall() called!\n", fPath.String()); ret = InitPath(path, &destination); parser_debug("Ret: %d %s\n", ret, strerror(ret)); @@ -499,14 +763,14 @@ status_t -PackageFile::WriteToPath(const char *path, ItemState *state) +PackageFile::DoInstall(const char *path, ItemState *state) { if (state == NULL) return B_ERROR; BPath &destination = state->destination; status_t ret = B_OK; - parser_debug("File: %s WriteToPath() called!\n", fPath.String()); + parser_debug("File: %s DoInstall() called!\n", fPath.String()); BFile file; if (state->status == B_NO_INIT || destination.InitCheck() != B_OK) { @@ -661,14 +925,14 @@ status_t -PackageLink::WriteToPath(const char *path, ItemState *state) +PackageLink::DoInstall(const char *path, ItemState *state) { if (state == NULL) return B_ERROR; status_t ret = B_OK; BSymLink symlink; - parser_debug("Symlink: %s WriteToPath() called!\n", fPath.String()); + parser_debug("Symlink: %s DoInstall() called!\n", fPath.String()); BPath &destination = state->destination; BDirectory *dir = &state->parent; Modified: haiku/trunk/src/apps/packageinstaller/PackageItem.h =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageItem.h 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageItem.h 2010-04-01 19:02:24 UTC (rev 36020) @@ -72,14 +72,14 @@ uint64 offset = 0, uint64 size = 0); virtual ~PackageItem(); - virtual status_t WriteToPath(const char* path = NULL, + virtual status_t DoInstall(const char* path = NULL, ItemState *state = NULL) = 0; virtual void SetTo(BFile* parent, const BString& path, uint8 type, uint32 ctime, uint32 mtime, uint64 offset = 0, uint64 size = 0); virtual const char* ItemKind() = 0; -protected: + protected: status_t InitPath(const char* path, BPath* destination); status_t HandleAttributes(BPath* destination, BNode* node, const char* header); @@ -91,6 +91,8 @@ uint64* tempSize, uint64* attrCSize, uint64* attrOSize, bool* attrStarted, bool* done); + status_t SkipAttribute(uint8 *buffer, bool *attrStarted, + bool *done); status_t ParseData(uint8* buffer, BFile* file, uint64 originalSize, bool* done); @@ -111,12 +113,34 @@ uint8 type, uint32 ctime, uint32 mtime, uint64 offset = 0, uint64 size = 0); - virtual status_t WriteToPath(const char* path = NULL, + virtual status_t DoInstall(const char* path = NULL, ItemState *state = NULL); virtual const char* ItemKind(); }; +class PackageScript : public PackageItem { +public: + PackageScript(BFile* parent, uint64 offset = 0, + uint64 size = 0, uint64 originalSize = 0); + + virtual status_t DoInstall(const char* path = NULL, + ItemState *state = NULL); + virtual const char* ItemKind(); + + thread_id GetThreadId() { return fThreadId; } + void SetThreadId(thread_id id) { fThreadId = id; } + +private: + status_t _ParseScript(uint8 *buffer, uint64 originalSize, + bool *done); + status_t _RunScript(uint8 *script, uint32 len); + + uint64 fOriginalSize; + thread_id fThreadId; +}; + + class PackageFile : public PackageItem { public: PackageFile(BFile* parent, const BString& path, @@ -125,7 +149,7 @@ uint32 platform, const BString& mime, const BString& signature, uint32 mode); - virtual status_t WriteToPath(const char* path = NULL, + virtual status_t DoInstall(const char* path = NULL, ItemState *state = NULL); virtual const char* ItemKind(); @@ -146,7 +170,7 @@ uint32 mtime, uint32 mode, uint64 offset = 0, uint64 size = 0); - virtual status_t WriteToPath(const char* path = NULL, + virtual status_t DoInstall(const char* path = NULL, ItemState *state = NULL); virtual const char* ItemKind(); Modified: haiku/trunk/src/apps/packageinstaller/PackageStatus.cpp =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageStatus.cpp 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageStatus.cpp 2010-04-01 19:02:24 UTC (rev 36020) @@ -92,10 +92,11 @@ PackageStatus::PackageStatus(const char *title, const char *label, - const char *trailing) + const char *trailing, BHandler *parent) : BWindow(BRect(200, 200, 550, 255), title, B_TITLED_WINDOW, B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_NOT_ZOOMABLE, 0), - fIsStopped(false) + fIsStopped(false), + fParent(parent) { SetLayout(new BGroupLayout(B_VERTICAL)); @@ -129,6 +130,13 @@ switch (msg->what) { case P_MSG_STOP: fIsStopped = true; + if (fParent != NULL) { + // If we have a parent defined, forward this message + BLooper *loop = fParent->Looper(); + if (loop != NULL) { + loop->PostMessage(msg, fParent); + } + } break; default: BWindow::MessageReceived(msg); Modified: haiku/trunk/src/apps/packageinstaller/PackageStatus.h =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageStatus.h 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageStatus.h 2010-04-01 19:02:24 UTC (rev 36020) @@ -31,7 +31,7 @@ class PackageStatus : public BWindow { public: PackageStatus(const char *title, const char *label = NULL, - const char *trailing = NULL); + const char *trailing = NULL, BHandler *parent = NULL); ~PackageStatus(); void MessageReceived(BMessage *msg); @@ -46,6 +46,7 @@ BStatusBar *fStatus; StopButton *fButton; bool fIsStopped; + BHandler *fParent; }; Modified: haiku/trunk/src/apps/packageinstaller/PackageView.cpp =================================================================== --- haiku/trunk/src/apps/packageinstaller/PackageView.cpp 2010-04-01 18:35:48 UTC (rev 36019) +++ haiku/trunk/src/apps/packageinstaller/PackageView.cpp 2010-04-01 19:02:24 UTC (rev 36020) @@ -63,7 +63,8 @@ //BView("package_view", B_WILL_DRAW, new BGroupLayout(B_HORIZONTAL)), fOpenPanel(new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_DIRECTORY_NODE, false)), - fInfo(ref) + fInfo(ref), + fInstallProcess(this) { _InitView(); @@ -121,7 +122,8 @@ // attaching the view to the window _GroupChanged(0); - fStatusWindow = new PackageStatus(T("Installation progress")); + fStatusWindow = new PackageStatus(T("Installation progress"), NULL, NULL, + this); // Show the splash screen, if present BMallocIO *image = fInfo.GetSplashScreen(); @@ -133,10 +135,10 @@ // Show the disclaimer/info text popup, if present BString disclaimer = fInfo.GetDisclaimer(); if (disclaimer.Length() != 0) { - PackageTextViewer *text = new PackageTextViewer(disclaimer.String()); + PackageTextViewer *text = new PackageTextViewer(disclaimer.String()); int32 selection = text->Go(); // The user didn't accept our disclaimer, this means we cannot continue. - if (selection == 0) { + if (selection == 0) { BWindow *parent = Window(); if (parent && parent->Lock()) parent->Quit(); @@ -153,36 +155,11 @@ case P_MSG_INSTALL: { fInstall->SetEnabled(false); + fInstallTypes->SetEnabled(false); + fDestination->SetEnabled(false); fStatusWindow->Show(); - BAlert *notify; - status_t ret = Install(); - if (ret == B_OK) { - notify = new BAlert("installation_success", - T("The package you requested has been successfully installed " - "on your system."), T("OK")); - notify->Go(); - fStatusWindow->Hide(); - - BWindow *parent = Window(); - if (parent && parent->Lock()) - parent->Quit(); - } - else if (ret == B_FILE_EXISTS) - notify = new BAlert("installation_aborted", - T("The installation of the package has been aborted."), T("OK")); - else { - notify = new BAlert("installation_failed", // TODO: Review this - T("The requested package failed to install on your system. This " - "might be a problem with the target package file. Please consult " - "this issue with the package distributor."), T("OK"), NULL, - NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); - fprintf(stderr, "Error while installing the package : %s\n", strerror(ret)); - } - notify->Go(); - fStatusWindow->Hide(); - fInstall->SetEnabled(true); - + fInstallProcess.Start(); break; } case P_MSG_PATH_CHANGED: @@ -204,6 +181,65 @@ } break; } + case P_MSG_I_FINISHED: + { + BAlert *notify = new BAlert("installation_success", + T("The package you requested has been successfully installed " + "on your system."), T("OK")); + + notify->Go(); + fStatusWindow->Hide(); + fInstall->SetEnabled(true); + fInstallTypes->SetEnabled(true); + fDestination->SetEnabled(true); + fInstallProcess.Stop(); + + BWindow *parent = Window(); + if (parent && parent->Lock()) + parent->Quit(); + break; + } + case P_MSG_I_ABORT: + { + BAlert *notify = new BAlert("installation_aborted", + T("The installation of the package has been aborted."), T("OK")); + notify->Go(); + fStatusWindow->Hide(); + fInstall->SetEnabled(true); + fInstallTypes->SetEnabled(true); + fDestination->SetEnabled(true); + fInstallProcess.Stop(); + break; + } + case P_MSG_I_ERROR: + { + BAlert *notify = new BAlert("installation_failed", // TODO: Review this + T("The requested package failed to install on your system. This " + "might be a problem with the target package file. Please consult " + "this issue with the package distributor."), T("OK"), NULL, + NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + fprintf(stderr, "Error while installing the package\n"); + notify->Go(); + fStatusWindow->Hide(); + fInstall->SetEnabled(true); + fInstallTypes->SetEnabled(true); + fDestination->SetEnabled(true); + fInstallProcess.Stop(); + break; + } + case P_MSG_STOP: + { + // This message is sent to us by the PackageStatus window, informing + // user interruptions. + // We actually use this message only when a post installation script + // is running and we want to kill it while it's still running + fStatusWindow->Hide(); + fInstall->SetEnabled(true); + fInstallTypes->SetEnabled(true); + fDestination->SetEnabled(true); + fInstallProcess.Stop(); + break; + } case B_REFS_RECEIVED: { entry_ref ref; @@ -247,122 +283,65 @@ } -status_t -PackageView::Install() +int32 +PackageView::ItemExists(PackageItem &item, BPath &path, int32 &policy) { - pkg_profile *type = static_cast<pkg_profile *>(fInfo.GetProfile(fCurrentType)); - uint32 n = type->items.CountItems(); + int32 choice = P_EXISTS_NONE; - fStatusWindow->Reset(n + 4); + switch (policy) { + case P_EXISTS_OVERWRITE: + choice = P_EXISTS_OVERWRITE; + break; - fStatusWindow->StageStep(1, "Preparing package"); + case P_EXISTS_SKIP: + choice = P_EXISTS_SKIP; + break; - InstalledPackageInfo packageInfo(fInfo.GetName(), fInfo.GetVersion()); + case P_EXISTS_ASK: + case P_EXISTS_NONE: + { + BString alertString = T("The "); - status_t err = packageInfo.InitCheck(); - err = B_ENTRY_NOT_FOUND; - if (err == B_OK) { - // The package is already installed, inform the user - BAlert *reinstall = new BAlert("reinstall", - T("The given package seems to be already installed on your system. " - "Would you like to uninstall the existing one and continue the " - "installation?"), T("Continue"), T("Abort")); + alertString << item.ItemKind() << T(" named \'") << path.Leaf() << "\' "; + alertString << T("already exists in the given path.\nReplace the file with " + "the one from this package or skip it?"); - if (reinstall->Go() == 0) { - // Uninstall the package - err = packageInfo.Uninstall(); - if (err != B_OK) { - fprintf(stderr, "Error on uninstall\n"); - return err; - } + BAlert *alert = new BAlert(T("file_exists"), alertString.String(), + T("Replace"), T("Skip"), T("Abort")); - err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true); - if (err != B_OK) { - fprintf(stderr, "Error on SetTo\n"); - return err; + choice = alert->Go(); + switch (choice) { + case 0: + choice = P_EXISTS_OVERWRITE; + break; + case 1: + choice = P_EXISTS_SKIP; + break; + default: + return P_EXISTS_ABORT; } - } - else { - // Abort the installation - return B_FILE_EXISTS; - } - } - else if (err == B_ENTRY_NOT_FOUND) { - err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true); - if (err != B_OK) { - fprintf(stderr, "Error on SetTo\n"); - return err; - } - } - else if (fStatusWindow->Stopped()) - return B_FILE_EXISTS; - else { - fprintf(stderr, "returning on error\n"); - return err; - } - fStatusWindow->StageStep(1, "Installing files and folders"); + if (policy == P_EXISTS_NONE) { + // TODO: Maybe add 'No, but ask again' type of choice as well? + alertString = T("Do you want to remember this decision for the rest of " + "this installation?\nAll existing files will be "); + alertString << ((choice == P_EXISTS_OVERWRITE) + ? T("replaced?") : T("skipped?")); - // Install files and directories - PackageItem *iter; - ItemState state; - uint32 i; - int32 choice; - BString label; + alert = new BAlert(T("policy_decision"), alertString.String(), + T("Yes"), T("No")); - packageInfo.SetName(fInfo.GetName()); - // TODO: Here's a small problem, since right now it's not quite sure - // which description is really used as such. The one displayed on - // the installer is mostly package installation description, but - // most people use it for describing the application in more detail - // then in the short description. - // For now, we'll use the short description if possible. - BString description = fInfo.GetShortDescription(); - if (description.Length() <= 0) - description = fInfo.GetDescription(); - packageInfo.SetDescription(description.String()); - packageInfo.SetSpaceNeeded(type->space_needed); - - fItemExistsPolicy = P_EXISTS_NONE; - - for (i = 0; i < n; i++) { - state.Reset(fItemExistsPolicy); // Reset the current item state - iter = static_cast<PackageItem *>(type->items.ItemAt(i)); - - err = iter->WriteToPath(fCurrentPath.Path(), &state); - if (err == B_FILE_EXISTS) { - // Writing to path failed because path already exists - ask the user - // what to do and retry the writing process - choice = _ItemExists(*iter, state.destination); - if (choice != P_EXISTS_ABORT) { - state.policy = choice; - err = iter->WriteToPath(fCurrentPath.Path(), &state); + int32 decision = alert->Go(); + if (decision == 0) + policy = choice; + else + policy = P_EXISTS_ASK; } + break; } - - if (err != B_OK) { - fprintf(stderr, "Error while writing path %s\n", fCurrentPath.Path()); - return err; - } - - if (fStatusWindow->Stopped()) - return B_FILE_EXISTS; - label = ""; - label << (uint32)(i + 1) << " of " << (uint32)n; - fStatusWindow->StageStep(1, NULL, label.String()); - - packageInfo.AddItem(state.destination.Path()); } - fStatusWindow->StageStep(1, "Finishing installation", ""); - - err = packageInfo.Save(); - if (err != B_OK) - return err; - - fStatusWindow->StageStep(1, "Done"); - - return B_OK; + return choice; } @@ -550,68 +529,6 @@ } -int32 -PackageView::_ItemExists(PackageItem &item, BPath &path) -{ - int32 choice = P_EXISTS_NONE; - - switch (fItemExistsPolicy) { - case P_EXISTS_OVERWRITE: - choice = P_EXISTS_OVERWRITE; - break; - - case P_EXISTS_SKIP: - choice = P_EXISTS_SKIP; - break; - - case P_EXISTS_ASK: - case P_EXISTS_NONE: - { - BString alertString = T("The "); - - alertString << item.ItemKind() << T(" named \'") << path.Leaf() << "\' "; - alertString << T("already exists in the given path.\nReplace the file with " - "the one from this package or skip it?"); - [... truncated: 96 lines follow ...]