source: other-projects/gs2-export-cdrom-installer/trunk/gsinstall.cpp

Last change on this file was 22957, checked in by ak19, 14 years ago

When doing a partial install on the harddrive, the CD Rom installer used to have issues when deleting and reinstalling (which we do for testing). This was because it would register the program with Windows registry and would try to re-install in the old location by getting that location from the Windows registry (it explicitly keeps track of existing installations). And in such cases where an installation already exists, it would not reinstall server.exe (or other executables), even though these might have been manually deleted. Now it no longer skips installing executables on subsequent re-installs.

  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1#include <vector>
2#include <string>
3
4// use the standard namespace
5#if !defined (GSDL_NAMESPACE_BROKEN)
6#if defined(GSDL_USE_OBJECTSPACE)
7using namespace ospace::std;
8#else
9using namespace std;
10#endif
11#endif
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <windows.h>
16#include <commctrl.h>
17
18#include "dirSelector.h"
19#include "common.h"
20#include "configFile.h"
21#include "File.h"
22#include "FilePath.h"
23#include "FileCopier.h"
24#include "launchApp.h"
25#include "unInstall.h"
26
27#include "gsRegistry.h"
28#include "gsPlatform.h"
29#include "gsProgman.h"
30#include "gsProfile.h"
31#include "gsManifest.h"
32
33char *app_cmdLine;
34static char app_name[] = "Greenstone Installer";
35HINSTANCE app_instance;
36HWND app_window;
37
38bool config_complete = false;
39
40dirSelector *rootSelector = NULL;
41
42typedef vector<dirSelector *> dirSelVector;
43
44class GSInstall : public installManager
45{
46private:
47 configureFile *configFile; // the installation configure file
48
49 FilePath *collectPath; // where the collection is currently installed
50 FilePath *installToPath; // the first cut at a destination folder
51 FilePath *sourcePath; // the root source folder
52 FilePath *gsdlSourcePath; // the gsdl folder in the source area
53 FilePath *destinationPath; // where the executables will be installed to
54 FilePath *dataDestPath; // where the collection data files will go
55
56 bool installVolume; // whether to install collection data
57 bool installFullVolume; // whether to install all collection data
58
59 dirSelVector selectedDirs; // configuration objects
60
61 gsPlatform platform; // platform information
62 gsRegistry *gsRegister; // registry
63 gsManifest *manifest; // manifest of files
64 gsProgramManager *progman; // program manager connection
65
66 string setupExe; // form to use for gs(s)setup.exe
67
68 void getSourcePath();
69 void getExistingInstall();
70public:
71 bool installExe;
72 bool offerNetscape;
73
74 GSInstall(bool uninstall);
75 FilePath *collectionPath();
76 FilePath *installPath();
77 FilePath *installSourcePath() { return this->sourcePath; }
78 string setupExeLeaf() { return this->setupExe; }
79 bool copyFiles();
80 bool updateRegistry();
81 bool updateProfiles();
82 bool updateProgman();
83 bool updateSetupExe();
84 bool installNetscape();
85 bool removeFailed(string file) { return this->manifest->undoFailed(file); }
86 void setDestination();
87 bool setUninstall();
88 void uninstall();
89 void setManifest();
90 void addSelectedDir(dirSelector *selector);
91};
92
93static string gsdl_DBHOME = "gdbmhome";
94static string gsdl_VLHOME = "gsdlhome";
95static string gsdl_STATIC = "staticpath";
96static string gsdl_COLLIST= "collections";
97
98void gsInstall_getDesktopExt(int *sx, int *sy)
99{
100 HDC dc = GetDC(GetDesktopWindow());
101 *sx = GetDeviceCaps(dc, HORZRES);
102 *sy = GetDeviceCaps(dc, VERTRES);
103 ReleaseDC(GetDesktopWindow(), dc);
104}
105
106/**
107 * Get various and nefarious details before we start trying an installation.
108 * The installation configuration file is read, we seek for an existing
109 * installation and reconfigure the default install settings appropriately.
110 * THe source path is also discovered.
111 */
112GSInstall::GSInstall(bool uninstall)
113 : installManager()
114{
115 // get the installation configuration file
116 this->configFile = new configureFile("install.cfg");
117
118 // set up "default" installation bits
119 this->installToPath = new FilePath("C:\\GSDL");
120 this->collectPath = NULL;
121 this->installExe = true; // we must install the exe files
122 this->offerNetscape = (this->configFile->getString("OfferNetscape") == "1");
123
124 // don't attempt this with windows 3.1
125 if (!this->platform.isWindows32s())
126 {
127 // get the registry
128 this->gsRegister = new gsRegistry(*this, *this->configFile);
129 this->setupExe = "gssetup.exe";
130 }
131 else
132 {
133 // TODO: in windows 3.1 we can't use the register as it isn't fully/properly
134 // implemented; we must get the existing path information from elsewhere
135 this->gsRegister = new gsRegistry(*this, *this->configFile);
136 this->setupExe = "gsetup.exe";
137 }
138
139 // assume for now we will install the full volume and the database
140 this->installVolume = true;
141 this->installFullVolume = true;
142
143 // detect any existing installation; unnecessary in uninstall (it's the
144 // "source" directory - see later
145 if (uninstall == false)
146 {
147 this->getExistingInstall();
148 }
149
150 // get where we are installing from
151 this->getSourcePath();
152
153 // if doing an uninstall we now know where the "uninstall" is
154 if (uninstall == true)
155 {
156 this->collectPath = this->sourcePath;
157 }
158
159 // get the manifest and program manager objects
160 if (uninstall)
161 {
162 this->manifest = new gsManifest(*this);
163 }
164 else
165 {
166 this->manifest = new gsManifest(*this, *this->sourcePath);
167 }
168 this->progman = new gsProgramManager(*this);
169
170 // tell manifest the number of bits in the raw windows environment; this may
171 // be required for loading certain modules such as winsock; the number of
172 // macros is over-the-top, but is more robust to unforeseen future requirements
173 /* if (platform.isWindows32s())
174 { this->manifest->expandMacro("WINBITS", "16");
175 this->manifest->expandMacro("WIN16", "16");
176 this->manifest->expandMacro("WIN32", "32");
177 }
178 else
179 { this->manifest->expandMacro("WINBITS", "32");
180 this->manifest->expandMacro("WIN16", "");
181 this->manifest->expandMacro("WIN32", "32");
182 }
183 */
184 // inform manifest of collection directory name
185 string colDir;
186 colDir = this->configFile->getString("CollectionDirName");
187 if (colDir != "")
188 {
189 this->manifest->expandMacro("COLDIRNAME", colDir);
190 }
191}
192
193/**
194 * Detect the presence of an existing installation
195 */
196void GSInstall::getExistingInstall()
197{
198 if (!this->platform.isWindows32s())
199 {
200 if (gsRegister->collectionInstalled())
201 {
202 // TODO: check if receptionist etc should be replaced or not
203
204 // TODO: check build version of the volume
205
206 // Get location of the library executable from the registry; this
207 // is thence used to get the location of llssite.cfg etc.
208 this->collectPath = this->gsRegister->collectionPath();
209 if (this->collectPath->pathString() != "")
210 {
211 this->installToPath = this->collectPath;
212 // we don't need to install the exe files
213 this->installExe = true; // we do now (was false before), else it doesn't allow a proper
214 // delete-and-reinstall cycle (doesn't install server.exe on subsequent installs then)
215 }
216 }
217 }
218}
219
220/**
221 * get the source path for this install; we derive this from the path of this
222 * executable (the installer)
223 */
224void GSInstall::getSourcePath()
225{
226 static char filename[512];
227 FilePath *exePath;
228
229 // get the filepath of this executable
230 GetModuleFileName(0, filename, 512);
231 exePath = new FilePath(filename);
232
233 // get the parent (i.e. the folder containing this executable) for the source
234 // folder
235 this->sourcePath = exePath->parent();
236
237 // get the gsdl source path
238 this->gsdlSourcePath = new FilePath(this->sourcePath->pathString(), "gsdl");
239 delete exePath;
240}
241
242/**
243 * Controls the actual copying of the files; manifests of what to install
244 * are read in from a configuration on the hard drive - this should be basically
245 * greenstone dependent, and void of any collection-specific rubbish!
246 */
247bool GSInstall::copyFiles()
248{
249 // ensure that we have got the required destination and that we can write there
250 if (this->installExe)
251 {
252// this->destinationPath->ensureWriteablePath();
253 }
254
255 // ensure that the destination path for the collection itself is available
256 if (this->installVolume)
257 {
258// this->dataDestPath->ensureWriteablePath();
259 }
260
261 // do the copy
262 return this->manifest->copy(this->gsdlSourcePath);
263}
264
265/**
266 * Fulfill registry update requirements; first the keys are set up, then they
267 * are filled with key item/value pairs.
268 */
269bool GSInstall::updateRegistry()
270{
271 // don't do registry stuff under windows 32s
272 // TODO: actually reinstate what is required
273 if (this->platform.isWindows32s())
274 {
275 return true;
276 }
277
278 // ensure all pertinent keys already exist for this volume/collection
279 this->gsRegister->ensureKeysExist();
280
281 // now move on to add key items
282 if (this->installExe)
283 {
284 // FilePath *serverPath = new FilePath(this->destinationPath->pathString(), "server.exe");
285 FilePath *serverPath = new FilePath(this->destinationPath->pathString(), "server.exe");
286 FilePath *setupPath = new FilePath(this->destinationPath->pathString(), this->setupExe);
287 FilePath *logPath = new FilePath(this->destinationPath->pathString(), "install.log");
288 string exeKeyPath, uninstallCmd, uninstallKeyPath;
289 gsPlatform platform;
290
291 // store the normal collection key information
292 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
293 this->gsRegister->collectKeyId(),
294 "library",
295 serverPath->pathString());
296
297 // This test is in fact for 9x or NT 4+; we're worried about the registry
298 // format, not about the shell as such
299 if (platform.isExplorerShell())
300 {
301 // get special app path key for windows 9x
302 exeKeyPath = this->gsRegister->exeKeyId("server.exe");
303
304 // ensure that the exe key exists
305 this->gsRegister->ensureKeyExists(HKEY_LOCAL_MACHINE, exeKeyPath);
306
307 // store default key for this application
308 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
309 exeKeyPath,
310 "",
311 serverPath->pathString());
312
313 // store path for this application
314 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
315 exeKeyPath,
316 "path",
317 this->destinationPath->pathString());
318
319 // create uninstall command line
320 uninstallCmd = setupPath->pathString() + " -u " + logPath->pathString();
321 uninstallKeyPath = this->gsRegister->uninstallKeyId(this->configFile->getString("CollectionName"));
322
323 // ensure uninstall key exists
324 this->gsRegister->ensureKeyExists(HKEY_LOCAL_MACHINE, uninstallKeyPath);
325
326 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE, uninstallKeyPath,
327 "DisplayName",
328 this->configFile->getString("CollectionName"));
329 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE, uninstallKeyPath,
330 "UninstallString", uninstallCmd);
331 }
332 delete setupPath;
333 delete serverPath;
334 delete logPath;
335 }
336
337 if (this->installVolume || this->installFullVolume)
338 {
339 // no actual key items are held in the volume key; for future use
340 }
341 return true;
342}
343
344/**
345 * Set the destination directory details
346 */
347void GSInstall::setDestination()
348{
349 // get configuration from the install wizard pages
350 this->destinationPath = new FilePath(this->selectedDirs[0]->selectedPath());
351 this->installFullVolume = this->selectedDirs[1]->getOption();
352 if (this->installFullVolume || true) // NB: always take path from 2nd dialog
353 {
354 this->dataDestPath = new FilePath(this->selectedDirs[1]->selectedPath());
355 }
356 else
357 {
358 this->dataDestPath = this->destinationPath;
359 }
360
361
362 // set the log for writing
363
364 FilePath *logPath = new FilePath(this->destinationPath->pathString(), "install.log");
365
366 this->setLogFile(logPath->pathString());
367
368 this->readLog();
369
370 delete logPath;
371
372}
373
374bool GSInstall::setUninstall()
375{
376 char tempPathStr[MAX_PATH];
377
378 // if we failed to get the Windows temporary directory, abort
379 if (GetTempPath(MAX_PATH, tempPathStr) == 0)
380 {
381 // TODO: abort!
382 return false;
383 }
384
385 FilePath tempPath(tempPathStr);
386
387 // if we are not running in the Windows temporary directory, copy this exe
388 // and the installation log to the temporary directory and run them there
389 if (*this->sourcePath != tempPath)
390 {
391 // copy this and install.log to temporary directory and restart
392 FilePath exePath(*this->sourcePath, this->setupExe);
393 FilePath logPath(*this->sourcePath, "install.log");
394
395 FilePath exeDest(tempPath, this->setupExe);
396 FilePath logDest(tempPath, "install.log");
397
398 CopyFile(exePath.cString(), exeDest.cString(), false);
399 CopyFile(logPath.cString(), logDest.cString(), false);
400
401 // now run the gssetup in the temporary directory
402 launchApp launchUninstall(exeDest);
403 launchUninstall.setCommandLine(" -u " + logPath.pathString());
404 launchUninstall.run(false, 0, "", "", false);
405
406 return false;
407 }
408
409 // open the log for reading from the directory this exe is now in (which
410 // will in fact be the temporary directory as outlined above)
411 FilePath *logPath = new FilePath(this->sourcePath->pathString(), "install.log");
412
413 this->setLogFile(logPath->pathString());
414 this->readLog();
415 delete logPath;
416
417 return true;
418}
419
420void GSInstall::uninstall()
421{
422 FilePath *iniPath;
423 string command;
424 stringArray params;
425
426 this->manifest = new gsManifest(*this); // get a manifest manager
427
428 iniPath = new FilePath(this->installRoot(), "llssite.cfg");
429 gsProfile gsdlProfile(*this, iniPath->pathString());
430
431 while ((command = this->popCommand(params)) != "")
432 {
433 if (!this->manifest->undoAction(command, params))
434 {
435 if (!gsdlProfile.undoAction(command, params))
436 {
437 if (!this->gsRegister->undoAction(command, params))
438 {
439 if (!this->progman->undoAction(command, params))
440 {
441 }
442 else
443 {
444
445 }
446 }
447 }
448 }
449 }
450
451}
452
453/**
454 * Collate the manifest for installation
455 */
456void GSInstall::setManifest()
457{
458 if (this->installExe)
459 {
460 this->manifest->selectGroup("library", *this->destinationPath);
461 }
462 if (this->installFullVolume)
463 {
464 this->manifest->selectGroup("supporting", *this->dataDestPath);
465 this->manifest->selectGroup("collection", *this->dataDestPath);
466 }
467 else
468 {
469 this->manifest->selectGroup("database", *this->dataDestPath);
470 }
471}
472
473/**
474 * Update the program manager/explorer shell with icons, groups etc for this
475 * collection/application
476 */
477bool GSInstall::updateProgman()
478{
479 string groupName;
480 string collectionName;
481
482 // if we managed to get a connection to the program manager (or explorer
483 // shell) then do the requisite updates
484 if (this->progman->connect())
485 {
486 // get group name from folders
487 groupName = this->configFile->getString("ProgramGroupName");
488 if (groupName == "")
489 {
490 // TODO: error handling
491 this->progman->disconnect();
492 return false;
493 }
494 collectionName = this->configFile->getString("CollectionName");
495 if (collectionName == "")
496 {
497 // TODO: error handling
498 this->progman->disconnect();
499 return false;
500 }
501
502 // add the group
503 if (!this->progman->addProgramGroup(groupName))
504 {
505 this->progman->disconnect();
506 return false;
507 }
508
509 // add a "server" icon
510 FilePath libraryPath(this->destinationPath->pathString(), "server.exe");
511 if (!this->progman->addIcon(groupName, collectionName, libraryPath.pathString(), "", ""))
512 {
513 }
514
515 FilePath readMePath(this->destinationPath->pathString(), "readme.txt");
516 if (!this->progman->addIcon(groupName, "ReadMe", readMePath.pathString(), "", ""))
517 {
518 }
519
520 FilePath supportPath(this->destinationPath->pathString(), "Support.htm");
521 if (!this->progman->addIcon(groupName, "Technical Support", supportPath.pathString(), "", ""))
522 {
523 }
524
525
526 FilePath uninstallPath(this->destinationPath->pathString(), this->setupExe);
527 FilePath logPath(this->destinationPath->pathString(), "install.log");
528 if (this->platform.isWindows32s() == false) // NB: don't add this shortcut under
529 // Windows32s as it causes a crash
530 //
531 // TODO: reinstate this with Win3.1
532 // happy code
533 {
534 if (!this->progman->addIcon(groupName, "Uninstall", uninstallPath.pathString(), " -u " + logPath.pathString(),
535 "Remove this Greenstone collection"))
536 {
537 }
538 }
539 else
540 { if (!this->progman->addIcon(groupName, "Uninstall", uninstallPath.pathString() + " -u " + logPath.pathString(), "",
541 "Remove this Greenstone collection"))
542 {
543 }
544 }
545 // disconnect from program manager
546 this->progman->disconnect();
547 }
548 return true;
549}
550
551/**
552 * Update the profile (ini) files associated with the program; these changes
553 * must be made after copying to files in the destination/executable folders
554 * as we can't write to CD-ROM (or at least we shouldn't - the install would
555 * become tainted).
556 */
557bool GSInstall::updateProfiles()
558{
559 FilePath *exePath;
560 FilePath *dataPath;
561 FilePath *iniPath;
562
563 // if we're installing the exe, then the exe path leads to the destination
564 // folder of this installation sequence; if not, then we pick it up from
565 // the existing location of that folder
566 if (this->installExe)
567 {
568 exePath = this->destinationPath;
569 }
570 else
571 {
572 exePath = this->collectPath;
573 }
574 iniPath = new FilePath(exePath->pathString(), "llssite.cfg");
575
576 // TODO: check if this 'if' structure is correct; suspect that it isn't quite
577 // as we may not be installing the volume, but exe etc individually;
578 // may be okay now but not future proof; also the destination directory
579 // may or may not be tied to the exe directory if that existed already.
580 // This all needs investigating
581 if (this->installVolume)
582 {
583 // Manufacture the appropriate section name now; an equivalent string
584 // is also constructed
585 string volumeSectionString = this->configFile->getString("CollectionName") +
586 "#" + this->configFile->getString("CollectionVolume");
587
588 // if we're installing the full data for the collection, then take the
589 // data path to be at the selected destination directory for the collection
590 // files; otherwise take the data path to be on the CD/distribution media
591 if (this->installFullVolume)
592 {
593 dataPath = this->dataDestPath;
594 }
595 else
596 {
597 dataPath = this->gsdlSourcePath;
598 }
599
600 // create a profile object to write to the llssite.cfg file
601 gsProfile gsdlProfile(*this, iniPath->pathString());
602
603 // if installing the executable, add the database and greenstone home
604 // directories
605 if (this->installExe)
606 {
607 // set the correct exe entries in llssite.cfg (in the gsdl section)
608 gsdlProfile.writeString("gsdl", "gsdlhome", dataPath->pathString());
609 gsdlProfile.writeString("gsdl", "gdbmhome", this->dataDestPath->pathString());
610 }
611
612 // set the correct collection volume entries in llssite.cfg
613 if (this->installVolume)
614 {
615 // note the collection in the general gsdl section; and add the undo item too
616 gsdlProfile.ensureListMember("gsdl", gsdl_COLLIST, volumeSectionString);
617
618 // note the volume data in its own section
619 gsdlProfile.writeString(volumeSectionString, "gsdlhome", dataPath->pathString());
620 gsdlProfile.writeString(volumeSectionString, "gdbmhome", this->dataDestPath->pathString());
621 gsdlProfile.writeString(volumeSectionString, "staticpath", dataPath->pathString());
622 }
623 }
624
625 // clean up
626 delete iniPath;
627
628 return true;
629}
630
631/**
632 * Ensure that we've installed the very latest setup executable @ the destination/
633 * greenstone folder
634 */
635bool GSInstall::updateSetupExe()
636{
637 FilePath *destExePath;
638 FilePath srcExeFile(*this->sourcePath, "gssetup.exe");
639
640 if (this->installExe)
641 {
642 destExePath = this->destinationPath;
643 }
644 else
645 {
646 destExePath = this->collectPath;
647 }
648 FilePath destExeFile(*destExePath, this->setupExe);
649
650 if (this->platform.isWindows32s() == false) // NB: Don't copy the setup executable
651 // to Windows32s as it causes a hang!
652 { if (!CopyFile(srcExeFile.cString(), destExeFile.cString(), false))
653 {
654 return false;
655 }
656 }
657 else
658 { // Windows 3.1 has a peculiar behaviour on calling CopyFile on an .exe;
659 // it doesn't. (Copy the file); thus we copy to an non-exe file and then
660 // rename it into an exe file; this works.
661 FilePath intExeFile(*destExePath, "gsetup.ex");
662 if (!CopyFile(srcExeFile.cString(), intExeFile.cString(), false))
663 {
664 return false;
665 }
666 MoveFile(intExeFile.cString(), destExeFile.cString());
667 }
668 return true;
669}
670
671bool GSInstall::installNetscape()
672{
673 FilePath netscape32Path(this->sourcePath->pathString(), "netscape\\n32e405.exe");
674 FilePath netscape16Path(this->sourcePath->pathString(), "netscape\\n16e405.exe");
675
676 launchApp launchNetscape(netscape32Path);
677 launchNetscape.platformApp(gsPlatform_WINDOWS32S, netscape16Path);
678 launchNetscape.platformApp(gsPlatform_WINDOWS, netscape16Path);
679 if (launchNetscape.run(true, 1,
680 "This library is displayed using a web browser. If you don't "
681 "currently have a web browser installed on your machine you may "
682 "install the Netscape 4.05 browser now.\n\n"
683 "Note that if your current browser was provided by your internet "
684 "service provider, you should install Netscape in a different "
685 "directory.\n\n"
686 "Would you like to install the Netscape 4.05 browser now?",
687 "Greenstone Installer", false) < 0)
688 {
689 // MessageBox(0, "Error", app_name, MB_OK);
690 return false;
691 }
692 return true;
693}
694
695FilePath *GSInstall::collectionPath()
696{
697 return this->collectPath;
698}
699
700FilePath *GSInstall::installPath()
701{
702 return this->installToPath;
703}
704
705void GSInstall::addSelectedDir(dirSelector *selector)
706{
707 this->selectedDirs.push_back(selector);
708}
709
710typedef struct
711{
712 dirSelector * dirSelect;
713} GSInstall_dirPathData;
714
715HGLOBAL loadDirBrowser()
716{
717 HRSRC resHdl;
718
719 resHdl = FindResource(app_instance, "MySaveAsDlg", RT_DIALOG);
720 return LoadResource(app_instance, resHdl);
721}
722
723void gsInstall_wizard_centre(HWND dialog)
724{
725 RECT rect;
726 int sx, sy;
727 int dx, dy;
728
729 gsInstall_getDesktopExt(&sx, &sy);
730 GetWindowRect(dialog, &rect);
731
732 dx = (sx - rect.right + rect.left) >> 1;
733 dy = (sy - rect.bottom + rect.top) >> 1;
734 SetWindowPos(dialog, 0, dx, dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
735}
736
737BOOL FAR PASCAL GSInstall_dlgproc(HWND Dialog, UINT Message, WPARAM wParam, LPARAM lParam)
738{
739 static bool first = true; // static variable to hack the position of the
740 // whole wizard; note doing this in the wizard
741 // property sheet initialisation DOES NOT WORK
742
743 switch (Message)
744 {
745 case WM_INITDIALOG:
746 {
747 dirSelector *selector = (dirSelector *) ((PROPSHEETPAGE *) lParam)->lParam;
748
749 SetDlgItemText(Dialog, dirpath_PROMPT, selector->prompt);
750 if (selector->optPrompt != NULL)
751 {
752 SetDlgItemText(Dialog, dirpath_OPTION, selector->optPrompt);
753 EnableWindow(GetDlgItem(Dialog, dirpath_BROWSE), true); // was false
754 EnableWindow(GetDlgItem(Dialog, dirpath_PATH), true); // was false
755 }
756 else
757 {
758 ShowWindow(GetDlgItem(Dialog, dirpath_OPTION), SW_HIDE);
759 }
760 SetWindowLong(Dialog, GWL_USERDATA, ((PROPSHEETPAGE *) lParam)->lParam);
761 SetDlgItemText(Dialog, dirpath_PATH, selector->selectedPath());
762
763 // if this is the first time this function is called, then centre the
764 // wizard into the centre of our screen.
765 if (first)
766 {
767 gsInstall_wizard_centre(GetParent(Dialog));
768 first = false;
769 }
770 }
771 return TRUE;
772
773 case WM_COMMAND:
774 switch (LOWORD(wParam))
775 {
776 case dirpath_BROWSE:
777 ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->requestPath(Dialog, "Select Directory");
778 SetDlgItemText(Dialog, dirpath_PATH, ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->selectedPath());
779 break;
780
781 case dirpath_OPTION:
782 ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->setOption(IsDlgButtonChecked(Dialog, dirpath_OPTION) == BST_CHECKED);
783 if (HIWORD(wParam) == BN_CLICKED && false)
784 // don't do the enable/disable these days
785 {
786 EnableWindow(GetDlgItem(Dialog, dirpath_BROWSE), IsDlgButtonChecked(Dialog, dirpath_OPTION));
787 EnableWindow(GetDlgItem(Dialog, dirpath_PATH), IsDlgButtonChecked(Dialog, dirpath_OPTION));
788 }
789 break;
790 }
791 break;
792
793 case WM_NOTIFY:
794 switch (((LPNMHDR) lParam)->code)
795 {
796 case PSN_SETACTIVE:
797 {
798 dirSelector *selector = ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA));
799
800 // bodge to set the dialogue path to the correct value
801 if (selector != rootSelector)
802 {
803 selector->setPath(rootSelector->selectedPath());
804 SetDlgItemText(Dialog, dirpath_PATH, selector->selectedPath());
805 }
806
807
808 PropSheet_SetWizButtons(GetParent(Dialog), (selector->isFirst() ? 0 : PSWIZB_BACK) |
809 (selector->isFinal() ? PSWIZB_FINISH : PSWIZB_NEXT));
810 }
811 break;
812
813 case PSN_KILLACTIVE:
814 break;
815
816 case PSN_WIZNEXT:
817 {
818 // note the path from the dialog
819 dirSelector *selector = ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA));
820 selector->setPathFromEdit(GetDlgItem(Dialog, dirpath_PATH));
821
822 // create a new FilePath object to check on the proposed destination
823 FilePath *path = new FilePath(selector->selectedPath());
824
825 // if the proposed destination doesn't exist, ask the user whether to
826 // create it now. TODO: actually create it if asked!
827 if (path->exists() == false)
828 {
829 char buffer[128];
830
831 sprintf(buffer, "Directory %s does not exist - create it?", selector->selectedPath());
832
833 // if the user cancelled that, then don't permit them to go to the next
834 // page of the wizard
835 if (MessageBox(0, buffer, app_name, MB_YESNO | MB_TOPMOST) == IDNO)
836 {
837 delete path;
838 SetWindowLong(Dialog, DWL_MSGRESULT, TRUE);
839 return TRUE;
840 }
841 }
842 delete path;
843 }
844 break;
845
846 case PSN_WIZFINISH:
847 {
848 // Finish the activity now
849 config_complete = true;
850 }
851 break;
852 }
853 break;
854 }
855 return FALSE;
856}
857
858void GSInstall_init_propertySheet(PROPSHEETPAGE &ppage, char *prompt, char *optPrompt,
859 char *title, GSInstall &installer, bool isFirst, bool isFinal)
860{
861 GSInstall_dirPathData *data = new GSInstall_dirPathData;
862
863 // create the directory Selector
864 data->dirSelect = new dirSelector(prompt, optPrompt, title, installer.installPath());
865 data->dirSelect->setFirst(isFirst);
866 data->dirSelect->setFinal(isFinal);
867
868 // if there is no extant root selector, make this it
869 if (rootSelector == NULL)
870 {
871 rootSelector = data->dirSelect;
872 }
873
874 // add this directory selector to the installer
875 installer.addSelectedDir(data->dirSelect);
876
877 // set up the rest of the page
878 ppage.dwSize = sizeof(PROPSHEETPAGE);
879 ppage.dwFlags = PSP_USETITLE;
880 ppage.hInstance = app_instance;
881 ppage.pszTemplate = "getDirPath";
882 ppage.pszIcon = 0;
883 ppage.pszTitle = app_name;
884 ppage.pfnDlgProc = (DLGPROC) GSInstall_dlgproc;
885 ppage.lParam = (LPARAM) data->dirSelect;
886 ppage.pfnCallback = NULL;
887}
888
889int CALLBACK GSInstall_wizardProc(HWND dialog, UINT Message, LPARAM lParameter)
890{
891 switch (Message)
892 {
893 case PSCB_INITIALIZED :
894 // Process PSCB_INITIALIZED
895 gsInstall_wizard_centre(dialog);
896 break ;
897
898 case PSCB_PRECREATE :
899 // Process PSCB_PRECREATE
900 break ;
901
902 default :
903 // Unknown message
904 break ;
905 }
906 return 0;
907}
908
909bool GSInstall_init_wizard(GSInstall &install)
910{
911 PROPSHEETHEADER pshead;
912 PROPSHEETPAGE ppage[2];
913 bool reply;
914
915 ZeroMemory(&pshead, sizeof(PROPSHEETHEADER));
916 pshead.dwSize = sizeof(PROPSHEETHEADER);
917 pshead.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK | PSH_USEHICON | PSH_WIZARD;
918 pshead.hwndParent = app_window;
919 pshead.hInstance = app_instance;
920 pshead.hIcon = NULL;
921 pshead.pszCaption = "Greenstone Installer";
922 pshead.nPages = 2;
923 pshead.nStartPage = 0;
924 if (install.installExe == true)
925 {
926 pshead.ppsp = ppage;
927 }
928 else // cheat as the executable etc. is already installed
929 {
930 pshead.ppsp = &ppage[1];
931 }
932 pshead.pfnCallback = GSInstall_wizardProc;
933
934 GSInstall_init_propertySheet( ppage[0],
935 "Choose a directory to install your "
936 "Greenstone software to.", NULL,
937 "Select", install, true, false);
938 GSInstall_init_propertySheet(ppage[1],
939 "Choose a directory to install the collection "
940 "files to.\n\n"
941 "You can also choose to install all the collection "
942 "files onto your computer hard drive.\n\n"
943 "If you do this, you will not have to load the "
944 "CD-ROM each time you use your Greenstone software "
945 "but more of your hard disk will be used.\n\n"
946 "Some collection files must be installed on your "
947 "computer.\n\n",
948 "Install all collection files",
949 "Select", install, false, true);
950
951 reply = (PropertySheet (&pshead) >= 0);
952 return reply;
953}
954
955long FAR PASCAL GSInstallWindProc(HWND Window, WORD Message, WPARAM wParameter, LPARAM lParameter)
956{
957 long reply = 0;
958
959 switch(Message)
960 {
961 case WM_CREATE:
962 {
963 ShowWindow(Window, SW_MAXIMIZE);
964 }
965 break;
966
967 case WM_COMMAND:
968 break;
969
970 case WM_USER:
971 {
972 if (strstr(app_cmdLine, "-u") != NULL)
973 {
974 // uninstall action
975
976 // skip past the -u option itself
977 char *at = strstr(app_cmdLine, "-u");
978 at += strlen("-u");
979
980 // extract the log file path from the command line
981 while (*at == ' ')
982 {
983 at ++;
984 }
985 string logPathString(at);
986 FilePath logPath(logPathString);
987
988 GSInstall install(true);
989 // if we're running in the temporary directory, do the uninstall
990 if (install.setUninstall())
991 {
992 install.uninstall();
993
994 // close the log to write back all changes to the log; if the file
995 // will be deleted, it must be closed; copying may also fail, so it's
996 // safer to close it before doing either.
997 install.recordLog();
998
999 // if the install is empty, terminate all final items (currently the
1000 // log and setup executables,
1001 if (install.isEmpty())
1002 {
1003 // delete the log file
1004 DeleteFile(logPath.cString());
1005
1006 // get it's parent to find where the gssetup executable will be
1007 FilePath *logParent = logPath.parent();
1008 FilePath uninstallPath(*logParent, install.setupExeLeaf());
1009
1010 // delete the gssetup executable
1011 DeleteFile(uninstallPath.cString());
1012
1013 // kill the original log parent directory if it should have died
1014 if (install.removeFailed(logParent->pathString()))
1015 {
1016 RemoveDirectory(logParent->cString());
1017 }
1018
1019 // dispose of the parent directory information
1020 delete logParent;
1021 }
1022
1023 // if not, then overwrite the original log with the modified one
1024 else
1025 {
1026 // do nothing - should be changed already!
1027 }
1028 }
1029 }
1030 // install wizard
1031 else
1032 {
1033 GSInstall install(false);
1034 GSInstall_init_wizard(install);
1035 if (config_complete == false)
1036 {
1037 MessageBox(0, "Install cancelled", app_name, MB_OK);
1038 }
1039 else
1040 {
1041 // configure the installation
1042 install.setDestination();
1043 install.setManifest();
1044
1045 if (!install.copyFiles()) // caused page fault; Windows 3.1
1046 { MessageBox(0, "Not enough space to install required files", "Greenstone Installer", MB_OK);
1047 install.abortLog();
1048 DestroyWindow(Window);
1049 return false;
1050 }
1051 install.updateProgman(); // caused divide overflow; Windows 3.1
1052 install.updateRegistry();
1053 install.updateProfiles();
1054 install.updateSetupExe(); // caused bomb-out of Windows; 3.1
1055
1056 // close log
1057 install.recordLog();
1058
1059 // do further actions
1060 if (install.offerNetscape == true) {
1061 install.installNetscape();
1062 }
1063
1064 MessageBox(0, "Installation complete.", "Greenstone Installer", MB_OK);
1065 }
1066 }
1067 DestroyWindow(Window);
1068 }
1069 break;
1070
1071 case WM_USER + 1:
1072 break;
1073
1074 case WM_CLOSE:
1075 /*if (!file_ischanged || IDCANCEL != file_query(Window, file_name))*/
1076 DestroyWindow(Window);
1077 return 0L;
1078
1079 case WM_DESTROY:
1080 {
1081 PostQuitMessage(0);
1082 }
1083 break;
1084
1085 default:
1086 return(DefWindowProc(Window, Message, wParameter, lParameter));
1087 }
1088 return reply;
1089}
1090
1091void GSInstall_init(HINSTANCE instance, int Show)
1092{
1093 int sx, sy;
1094
1095 gsInstall_getDesktopExt(&sx, &sy);
1096
1097 app_window = CreateWindow("GSInstall:Main", "Greenstone Installer",
1098 WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | CS_DBLCLKS,
1099 0, 0,
1100 sx, sy,
1101 NULL,
1102 NULL,
1103 app_instance,
1104 NULL);
1105
1106 ShowWindow(app_window, Show);
1107
1108 PostMessage(app_window, WM_USER, 0, 0L);
1109}
1110
1111void WinClassInit(void)
1112{
1113 WNDCLASS Class;
1114
1115 Class.lpszClassName = "GSInstall:Main";
1116 Class.hInstance = app_instance;
1117 Class.lpfnWndProc = (WNDPROC) GSInstallWindProc;
1118 Class.hCursor = LoadCursor(NULL, IDC_ARROW);
1119 Class.hIcon = NULL; //LoadIcon(app_instance, "GSInstall");
1120 Class.lpszMenuName = NULL;
1121 Class.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE+1);
1122 Class.style = 0;
1123 Class.cbClsExtra = 0;
1124 Class.cbWndExtra = 0;
1125 RegisterClass(&Class);
1126}
1127
1128int PASCAL WinMain(HINSTANCE Current,
1129 HINSTANCE Previous,
1130 LPSTR CmdLine,
1131 int CmdShow)
1132{
1133 MSG msg;
1134
1135 gsPlatform p;
1136
1137 if (p.isWindows32s())
1138 {
1139 char filename[512];
1140 FilePath *exePath;
1141 FilePath *dirPath;
1142
1143 // get the filepath of this executable
1144 GetModuleFileName(0, filename, 512);
1145 exePath = new FilePath(filename);
1146
1147 // get the parent (i.e. the folder containing this executable) for the source
1148 // folder
1149 dirPath = exePath->parent();
1150 delete exePath;
1151
1152 exePath = new FilePath(dirPath->cString(), "install.log");
1153
1154 if (exePath->exists())
1155 { app_cmdLine = (char *) malloc(256);
1156 lstrcpy(app_cmdLine, "gsetup.exe -u ");
1157 lstrcat(app_cmdLine, exePath->cString());
1158 }
1159 else
1160 { app_cmdLine = (char *) malloc(1);
1161 app_cmdLine[0] = '\0';
1162 }
1163
1164 delete exePath;
1165 delete dirPath;
1166 }
1167 else
1168 { app_cmdLine = (char *) malloc(lstrlen(CmdLine) + 1);
1169 lstrcpy(app_cmdLine, CmdLine);
1170 }
1171
1172 app_instance = Current;
1173 /* -- init application */
1174 if (!Previous)
1175 {
1176 WinClassInit();
1177 InitCommonControls();
1178 }
1179
1180 /* -- init instance */
1181 GSInstall_init(Current, CmdShow);
1182
1183 while (GetMessage(&msg, NULL, 0, 0))
1184 {
1185 TranslateMessage(&msg);
1186 DispatchMessage(&msg);
1187 }
1188 return (int) msg.wParam;
1189}
Note: See TracBrowser for help on using the repository browser.