home *** CD-ROM | disk | FTP | other *** search
Wrap
/* Miranda Installer - Installs nightlies and Miranda addons. Copyright (C) 2002-2003 Goblineye Entertainment Authors: Saar (Tornado) and Kai (kai_b) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Install.h" #pragma hdrstop // gave problems in headers. this is nicer. const char* INSTALLATION_TYPES_ARR[10] = { "Plugin", "Icon", "Sound", "LangPack", "Tool", "Source", "Doc", "Skin", "Nightly", NULL }; const char* INSTALLATION_EXTS_ARR[10] = { "mir", "mii", "mis", "mil", "mit", "mio", "mic", "mik", "min", NULL }; void XMLHandler_GetAddonBasicInfo(byte bMsg,char *lpszData,unsigned long ulLen,LPARAM lParam) { static byte s_bDepth; // depth of script. not really used the way you think it's used :) static char s_szElement[32]; // long enough static bool s_fOptPackage; // is current package optional? static GENERAL_FILELIST *s_pFileList; // local filelist, per package. we don't assume crap, a package might not have any files (scripter's fault, but we should still watch out) switch(bMsg) { case XMLPARSER_NOTIFY_STARTELEMENT: { if ((s_bDepth == 2) || (s_bDepth == 5)) // inside info or packageinfo { // remember the element type if (ulLen < 32) // just the right size { strncpy(s_szElement,lpszData,ulLen); s_szElement[ulLen] = '\0'; if (lstrcmpi(s_szElement,"optional") == 0) // optional empty tag { s_fOptPackage = true; } } else { s_szElement[0] = '\0'; // no element, can't be (our tags are shorter) } } else if (_strnicmp(lpszData,"installscript",ulLen) == 0) // going into script { if (s_bDepth == 0) { s_bDepth++; } } else if (_strnicmp(lpszData,"info",ulLen) == 0) // info realm { if (s_bDepth == 1) { s_bDepth++; } } else if (_strnicmp(lpszData,"packageinfo",ulLen) == 0) // packageinfo realm { s_fOptPackage = false; s_pFileList = NULL; if (s_bDepth == 1) { s_bDepth = 5; // skip to 5 (made it up :)) } } } break; case XMLPARSER_NOTIFY_ENDELEMENT: { if (_strnicmp(lpszData,"info",ulLen) == 0) // done with info { if (s_bDepth == 2) { s_bDepth--; } } else if (_strnicmp(lpszData,"installscript",ulLen) == 0) // done with installscript { if (s_bDepth == 1) { s_bDepth--; } } else if (_strnicmp(lpszData,"packageinfo",ulLen) == 0) // done with packageinfo { if (s_bDepth == 5) { ADDON_BASICINFO *basicInfo = (ADDON_BASICINFO*)lParam; if (!s_fOptPackage) // no optional tag found { basicInfo->wMainPackages++; // another main package } basicInfo->wTotalPackages++; // another total package // examine s_pFileList if (s_pFileList != NULL) // got files { if (basicInfo->pFileList == NULL) { basicInfo->pFileList = s_pFileList; } else // add at end of master list { GENERAL_FILELIST *pLast = basicInfo->pFileList; while (pLast->next != NULL) { pLast = pLast->next; } pLast->next = s_pFileList; } s_pFileList = NULL; // disallocation isn't done by us } else { basicInfo->fGotEmptyPackage = true; // usually a deal-breaker (package with no files) } s_bDepth = 1; } } } break; case XMLPARSER_NOTIFY_DATA: { if (ulLen > 0) // do we have info in there? { ADDON_BASICINFO *basicInfo = (ADDON_BASICINFO*)lParam; if (s_bDepth == 2) // info { if (lstrcmpi(s_szElement,"name") == 0) // name, let's get it { basicInfo->lpszName = new char[ulLen + 1]; lstrcpyn(basicInfo->lpszName,lpszData,ulLen+1); } else if (lstrcmpi(s_szElement,"author") == 0) // author, let's get it { basicInfo->lpszAuthor = new char[ulLen + 1]; lstrcpyn(basicInfo->lpszAuthor,lpszData,ulLen+1); } else if (lstrcmpi(s_szElement,"version") == 0) // version, let's get it { basicInfo->lpszVersion = new char[ulLen + 1]; lstrcpyn(basicInfo->lpszVersion,lpszData,ulLen+1); } else if (lstrcmpi(s_szElement,"type") == 0) // type, let's get it { basicInfo->lpszType = new char[ulLen + 1]; lstrcpyn(basicInfo->lpszType,lpszData,ulLen+1); } } else if (s_bDepth == 5) // packageinfo { if (lstrcmpi(s_szElement,"file") == 0) // file, let's get it { GENERAL_FILELIST *node = new GENERAL_FILELIST; node->lpFilename = new char[ulLen + 1]; lstrcpyn(node->lpFilename,lpszData,ulLen+1); Switch_Slashes(node->lpFilename,true); // switch slashes (to slashes) // always add at beginning node->next = s_pFileList; s_pFileList = node; } } } } break; } } // on success, returns an allocated structure with basic information about // the given addon // on failure, returns NULL // errors are *NOT* shown here! static ADDON_BASICINFO *Install_GetBasicInfo(const char *lpFilePath) { // upon allocation, the structure MUST be memset to 0, to prevent any problems later unzFile hFile = unzOpen(lpFilePath); if (hFile != NULL) { ADDON_BASICINFO *basicInfo = new ADDON_BASICINFO; // allocate it ZeroMemory(basicInfo,sizeof(ADDON_BASICINFO)); if (unzLocateFile(hFile,INSTALLSCRIPT_FILENAME,2) == UNZ_OK) // try to find the file (insensitive) { // we got a script! unz_file_info fileInfo; unzGetCurrentFileInfo(hFile,&fileInfo,NULL,0,NULL,0,NULL,0); // we need its size if (fileInfo.uncompressed_size > 0) // valid { basicInfo->fScripted = true; // script is good. if (unzOpenCurrentFile(hFile) == UNZ_OK) { char *lpScriptBuffer = new char[fileInfo.uncompressed_size + 1]; // the script can't be that big // that's the basic assumption here int iResult = unzReadCurrentFile(hFile,lpScriptBuffer,fileInfo.uncompressed_size); if (iResult < 0) // error { // no script, some error delete [] lpScriptBuffer; delete basicInfo; unzCloseCurrentFile(hFile); unzClose(hFile); return(NULL); } if (unzCloseCurrentFile(hFile) == UNZ_CRCERROR) { // no script, CRC error delete [] lpScriptBuffer; delete basicInfo; unzClose(hFile); return(NULL); } lpScriptBuffer[fileInfo.uncompressed_size] = '\0'; // close it XMLParser_FeedDocument(XMLHandler_GetAddonBasicInfo,lpScriptBuffer,(LPARAM)basicInfo); delete [] lpScriptBuffer; } } } unzClose(hFile); if (!basicInfo->fScripted) // no script { basicInfo->wMainPackages = basicInfo->wTotalPackages = 1; // one package } if (basicInfo->lpszName == NULL) { char *lpFileName = strrchr(lpFilePath,'\\'); if (lpFileName != NULL) { lpFileName++; // advance basicInfo->lpszName = new char[lstrlen(lpFileName) + 1]; // len until the end lstrcpy(basicInfo->lpszName,lpFileName); } } // now the type has to be handled too // let's check if we have a type from before, if we do, we still might have to check the extension if (basicInfo->lpszType != NULL) // previous type! { for (int index=0;INSTALLATION_TYPES_ARR[index] != NULL;index++) { if (lstrcmpi(INSTALLATION_TYPES_ARR[index],basicInfo->lpszType) == 0) // try to match the scripted type with one of our own { // we got a match, we're also going to delete what we got, and use our type instead // ours will ALWAYS look nice :) delete [] basicInfo->lpszType; basicInfo->lpszType = new char[lstrlen(INSTALLATION_TYPES_ARR[index]) + 1]; lstrcpy(basicInfo->lpszType,INSTALLATION_TYPES_ARR[index]); // copy it break; } } if (INSTALLATION_TYPES_ARR[index] == NULL) // argg? not in the list... kill it { delete [] basicInfo->lpszType; basicInfo->lpszType = NULL; } } if (basicInfo->lpszType == NULL) // could've been non-NULL before, and it is now { char *lpExtension = strrchr(lpFilePath,'.'); if (lpExtension != NULL) { lpExtension++; // advance for (int index=0;INSTALLATION_EXTS_ARR[index] != NULL;index++) { if (lstrcmpi(INSTALLATION_EXTS_ARR[index],lpExtension) == 0) // try to match the extension { // weee basicInfo->lpszType = new char[lstrlen(INSTALLATION_TYPES_ARR[index]) + 1]; lstrcpy(basicInfo->lpszType,INSTALLATION_TYPES_ARR[index]); // corresponding type from type array break; } } } } return(basicInfo); } return(NULL); } // frees and deallocates a basic info structure static void Free_BasicInfo(ADDON_BASICINFO **pBasicInfo) { if ((pBasicInfo == NULL) || (*pBasicInfo == NULL)) { return; } if ((*pBasicInfo)->lpszName != NULL) { delete [] (*pBasicInfo)->lpszName; } if ((*pBasicInfo)->lpszAuthor != NULL) { delete [] (*pBasicInfo)->lpszAuthor; } if ((*pBasicInfo)->lpszVersion != NULL) { delete [] (*pBasicInfo)->lpszVersion; } if ((*pBasicInfo)->lpszType != NULL) { delete [] (*pBasicInfo)->lpszType; } Cleanup_FileList(&((*pBasicInfo)->pFileList)); // release filelist delete *pBasicInfo; *pBasicInfo = NULL; } static void InstallList_AddToList(HWND hListView,ADDON_BASICINFO *pBasicInfo,const char *lpFilePath) { LVITEM lvItem; // allocate to lParam ADDON_LISTINFO *pListInfo = new ADDON_LISTINFO; ZeroMemory(pListInfo,sizeof(ADDON_LISTINFO)); for (byte index=0;INSTALLATION_TYPES_ARR[index] != NULL;index++) { if (lstrcmpi(INSTALLATION_TYPES_ARR[index],pBasicInfo->lpszType) == 0) // match the type { pListInfo->bType = index; // const array is synced with types break; } } // now for file path pListInfo->lpFilePath = new char[lstrlen(lpFilePath) + 1]; // wee lstrcpy(pListInfo->lpFilePath,lpFilePath); // make a copy lvItem.iSubItem = 0; lvItem.mask = LVIF_PARAM | LVIF_TEXT; lvItem.iItem = ListView_GetItemCount(hListView); // this doesn't matter since we're sorting later lvItem.pszText = pBasicInfo->lpszName; lvItem.lParam = (LPARAM)pListInfo; lvItem.iItem = ListView_InsertItem(hListView,&lvItem); // lvItem.iItem won't have to be touched anymore lvItem.mask = LVIF_TEXT; lvItem.iSubItem = 1; if (pBasicInfo->lpszAuthor == NULL) // might not be filled { lvItem.pszText = Translate("N/A"); } else { lvItem.pszText = pBasicInfo->lpszAuthor; } ListView_SetItem(hListView,&lvItem); lvItem.iSubItem = 2; if (pBasicInfo->lpszVersion == NULL) // might not be filled { lvItem.pszText = Translate("N/A"); } else { lvItem.pszText = pBasicInfo->lpszVersion; } ListView_SetItem(hListView,&lvItem); lvItem.iSubItem = 3; lvItem.pszText = Translate(pBasicInfo->lpszType); ListView_SetItem(hListView,&lvItem); lvItem.iSubItem = 4; char szTemp[32]; sprintf(szTemp,"%d/%d",pBasicInfo->wMainPackages,pBasicInfo->wTotalPackages); lvItem.pszText = szTemp; ListView_SetItem(hListView,&lvItem); lvItem.iSubItem = 5; lvItem.pszText = Translate("Waiting..."); ListView_SetItem(hListView,&lvItem); SendMessage(GetParent(hListView),WM_USER+112,true,0); // let parent know we have a change in num of items } void InstallList_AddFile(HWND hwndDlg,const char *lpFilePath,bool fQuiet) { // before we even look at the file // we'll make sure it's not in the list already HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS); LVITEM lvItem; lvItem.mask = LVIF_PARAM; lvItem.iSubItem = 0; for (int index=0,iCount=ListView_GetItemCount(hListView);index < iCount;index++) { lvItem.iItem = index; ListView_GetItem(hListView,&lvItem); if (lstrcmpi(((ADDON_LISTINFO*)lvItem.lParam)->lpFilePath,lpFilePath) == 0) // same! { return; // not going to install it } } ADDON_BASICINFO *pBasicInfo = Install_GetBasicInfo(lpFilePath); if (pBasicInfo != NULL) { // right, now we check for deal breakers // 1. if ((pBasicInfo->lpszType == NULL) || (pBasicInfo->lpszName == NULL)) // unknown type { if (!fQuiet) // only notice if this isn't quiet mode { MessageBox(hwndDlg,Translate("Type of package is unknown, file cannot be installed! (Does the file has a script? If not, does it have the right extension?)"),Translate("Miranda Installer Error"),MB_ICONWARNING); } Free_BasicInfo(&pBasicInfo); return; } // 2. if ((pBasicInfo->fScripted) && (pBasicInfo->wMainPackages == 0)) // no main packages are defined! { if (!fQuiet) { MessageBox(hwndDlg,Translate("No Main packages were found in file, cannot be installed! (Probably a bad script)"),Translate("Miranda Installer Error"),MB_ICONWARNING); } Free_BasicInfo(&pBasicInfo); return; } // 3. if (pBasicInfo->fGotEmptyPackage) // empty package!? (we *KNOW* that we have a script, since this is inited to 0 when we start) { if (!fQuiet) { MessageBox(hwndDlg,Translate("One of the packages in the zip contains no files, cannot be installed! (Probably a bad script)"),Translate("Miranda Installer Error"),MB_ICONWARNING); } Free_BasicInfo(&pBasicInfo); return; } // 4. (go through file list and find invalid and dupes, argg) if ((pBasicInfo->fScripted) && (pBasicInfo->pFileList != NULL)) { GENERAL_FILELIST *pFileList = pBasicInfo->pFileList; unzFile hFile = unzOpen(lpFilePath); byte bError = 0; // 1 - invalid file, 2 - dupe (pFileList->lpFilename will point in either case - for formatting purposes) if (hFile != NULL) { while (pFileList != NULL) { if (unzLocateFile(hFile,pFileList->lpFilename,2) != UNZ_OK) // argg { bError = 1; // invalid break; } // ok now we have to go through the file list and see if this file exists somewhere GENERAL_FILELIST *pCompFileList = pBasicInfo->pFileList; while (pCompFileList != NULL) { if ((pCompFileList != pFileList) && (lstrcmp(pFileList->lpFilename,pCompFileList->lpFilename) == 0)) // argg { bError = 2; break; } pCompFileList = pCompFileList->next; } if (bError == 2) { break; } pFileList = pFileList->next; } unzClose(hFile); } if (bError) // some kind of error { if (!fQuiet) { char szMsg[512]; Switch_Slashes(pFileList->lpFilename,false); // switch it again, to make it displayable if (bError == 1) // invalid { wsprintf(szMsg,Translate("The file \"%s\" used in the script was not found in the zip file, can't install file"),pFileList->lpFilename); } else // dupe { wsprintf(szMsg,Translate("The file \"%s\" is used more than once in the script, can't install file"),pFileList->lpFilename); } MessageBox(hwndDlg,szMsg,Translate("Miranda Installer Error"),MB_ICONWARNING); } Free_BasicInfo(&pBasicInfo); return; } } // ok now let's add this one to the list InstallList_AddToList(hListView,pBasicInfo,lpFilePath); Free_BasicInfo(&pBasicInfo); } else // something wrong { // MessageBox(hwndDlg,Translate("File type is invalid!"),Translate("Miranda Installer Error"),MB_ICONWARNING); } } void InstallList_AddDirectory(HWND hwndDlg,const char *lpDirectoryPath) { char szSearchDir[MAX_PATH + 1]; HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS); // go over all extensions lstrcpy(szSearchDir,lpDirectoryPath); lstrcat(szSearchDir,"\\*.zip"); int index = 0; // needed, otherwise we'd miss the NULL do // this way we can stick one more type (.zip) that's not "officially" included (zip files can also be used, if they have installation scripts) { // find files WIN32_FIND_DATA findData; HANDLE hFile = FindFirstFile(szSearchDir,&findData); if (hFile != INVALID_HANDLE_VALUE) { do { // find info and add to list here lstrcpy(szSearchDir,lpDirectoryPath); // lstrcat(szSearchDir,"\\"); // causes trouble // no need for it either lstrcat(szSearchDir,findData.cFileName); InstallList_AddFile(hwndDlg,szSearchDir,true); // install the file } while (FindNextFile(hFile,&findData)); } // go to next file type if (INSTALLATION_EXTS_ARR[index] != NULL) { lstrcpy(szSearchDir,lpDirectoryPath); lstrcat(szSearchDir,"\\*."); lstrcat(szSearchDir,INSTALLATION_EXTS_ARR[index]); // add extension } } while (INSTALLATION_EXTS_ARR[index++] != NULL); }