#ifndef CONFIG_H #define CONFIG_H /* * @(#)Config.h * * This file is part of webCDwriter - Network CD/DVD Writing. * * Copyright (C) 1999-2006 Jörg P. M. Haeger * * webCDwriter is free software. See CDWserver.cpp for details. * * Jörg Haeger, 25.05.1999 */ #include #include #include "Boolean.h" #include "Error.h" #include "File.h" #include "Integer.h" #include "Process.h" #include "String.h" #include "Support.h" #include "Types.h" #include "Log.h" #include "config.h" const int maxNumOfWriters = 20; const int PATH_COPY = 1, PATH_DUMMY = 2, PATH_ROOT = 4; const char ATA[] = "ATA", BINDIR[] = "BINDIR", CDRECORDWANTSTAO[] = "cdrecordWantsTAO", DEFAULTSPEED[] = "defaultSpeed", DEFAULTSPEEDAUDIO[] = "defaultSpeedAudio", DEFAULTSPEEDCDRW[] = "defaultSpeedCD-RW", DVDPlusR_DL[] = "DVD+R_DL", DVDSUPPORT[] = "DVDSupport", IMPORT_IMAGE_SUPPORT[] = "importImageSupport", PASSWORD[] = "password", SIMULATION[] = "simulation", TOOLSDIR[] = "TOOLSDIR"; /** * This class delivers the values of the configuration file. * * @version 20050426 * @author Jörg P. M. Haeger */ class Config { public: String cdrdaoInfo, cdrecordInfo, growisofsInfo, mkisofsInfo, mpg123Info, ogg123Info, soxInfo; private: pthread_mutex_t mutex; String configDir, fileName; String firstError, firstErrorHelp; int firstErrorPriority; boolean hasReader; String mimeTypes; int lastDeviceNo, numOfWriters; String text; boolean HTML; // values boolean accounts; String accountsFile; String debugCache; const char *eMailOnError; int gid; const char *group; boolean imageOnTheFlyCache; String infoFile; String insertCD_MsgFile; boolean laserOn; int logLevelCache; int maxCopies; int maxMBytesInSpoolDirCache; int maxMBytesPerSessionCache; int maxSpeed; boolean mkisofsWithUTF8; const char *organization; boolean removeSessions; int reservedMBytesCache; bool setuid; unsigned short int statusPortNo; boolean supportFormCache; int uid; const char *user; bool userSave; class Device { public: String dev, id, rawDevice, imageFile, info, supports; public: int no, CD, CDR, CDRW, DVD, DVDMinusR, DVDMinusRW, DVDPlusR, DVDPlusRW, DVDRAM, DVDPlusR_DL; Device(): dev(""), rawDevice(""), info("") { no = 1; CD = CDR = CDRW = 0; DVD = DVDMinusR = DVDMinusRW = DVDPlusR = DVDPlusRW = 0; DVDRAM = DVDPlusR_DL = 0; } public: bool isReadOnlyDevice() { return (CD || DVD) && !CDR && !CDRW && !DVDMinusR && !DVDMinusRW && !DVDPlusR && !DVDPlusRW && !DVDRAM && !DVDPlusR_DL; } }; Device writers[maxNumOfWriters]; class Item { public: String key; Object &defaultValue, &editor, &config, &active; String error; int lineInConfig; public: Item(const char *key, boolean value): key(key), defaultValue(*new Boolean(value)), editor(*new Boolean(value)), config(*new Boolean(value)), active(*new Boolean(value)), error("") { lineInConfig = -1; } public: Item(const char *key, int value): key(key), defaultValue(*new Integer(value)), editor(*new Integer(value)), config(*new Integer(value)), active(*new Integer(value)), error("") { lineInConfig = -1; } public: Item(const char *key, const char *value, const char *example = ""): key(key), defaultValue(*new String(value)), editor(*new String(value)), config(*new String(value)), active(*new String(value)), error("") { lineInConfig = -1; } public: void activateConfig() { if (active.instanceof("Boolean")) (Boolean &)active = (Boolean &)config; else if (active.instanceof("Integer")) (Integer &)active = (Integer &)config; else (String &)active = (String &)config; } public: bool isDefault() { if (active.instanceof("Boolean")) return ((Boolean &)config).booleanValue() == ((Boolean &)defaultValue).booleanValue(); else if (active.instanceof("Integer")) return ((Integer &)config).intValue() == ((Integer &)defaultValue).intValue(); else return ((String &)config).equals((String &)defaultValue); } public: void resetEditor() { if (editor.instanceof("Boolean")) (Boolean &)editor = (Boolean &)config; else if (editor.instanceof("Integer")) (Integer &)editor = (Integer &)config; else (String &)editor = (String &)config; } private: void setConfig(boolean value) { (Boolean &)config = new Boolean(value); } private: void setConfig(int value) { (Integer &)config = new Integer(value); } private: void setConfig2(const char *value) { (String &)config = new String(value); } public: void setConfig(const char *value) { if (active.instanceof("Boolean")) setConfig(Boolean::getBoolean(value)); else if (active.instanceof("Integer")) setConfig(Integer::getInt(value)); else setConfig2(value); } public: void setDefault(int value) { (Integer &) defaultValue = new Integer(value); } private: void setEditor(boolean value) { (Boolean &)editor = new Boolean(value); } private: void setEditor(int value) { (Integer &)editor = new Integer(value); } private: void setEditor2(String &value) { (String &)editor = value; } public: void setEditor(String &value) { if (active.instanceof("Boolean")) setEditor(Boolean::getBoolean(value)); else if (active.instanceof("Integer")) setEditor(Integer::getInt(value.getBytes())); else setEditor2(value); } public: void updateConfig() { if (key.equals(PASSWORD)) return; if (editor.instanceof("Boolean")) (Boolean &)config = (Boolean &)editor; else if (editor.instanceof("Integer")) (Integer &)config = (Integer &)editor; else (String &)config = (String &)editor; } }; Item *items[200]; int numOfItems; public: Config(); public: void activateConfig() { for (int i = 0; i < numOfItems; i++) items[i]->activateConfig(); debugCache = getActiveValue("debug").getBytes(); imageOnTheFlyCache = getActiveBoolean("imageOnTheFly"); logLevelCache = getActiveInt("logLevel"); maxMBytesInSpoolDirCache = getActiveInt("maxMBytesInSpoolDir"); maxMBytesPerSessionCache = getActiveInt("maxMBytesPerSession"); reservedMBytesCache = getActiveInt("reservedMBytes"); supportFormCache = getActiveBoolean("supportForm"); } private: int add(Item *item) { const char *key = item->key.getBytes(); int index = search(key); if (index >= 0) throw new Exception(S.e + key + " already exists"); for (index = 0; index < numOfItems; index++) if (items[index]->key.compareToIgnoreCase(key) > 0) break; for (int i = numOfItems; i > index; i--) items[i] = items[i - 1]; numOfItems++; items[index] = item; items[index]->key = key; // items[index]->isDefault = true; return index; } private: void add(const char *key, boolean value) { add(new Item(key, value)); } private: void add(const char *key, const char *value) { add(new Item(key, value)); } private: void add(const char *key, String *value) { add(new Item(key, value->getBytes())); delete value; } private: void add(const char *key, int value) { add(new Item(key, value)); } private: int addDevice(String &dev, String &rawDevice); private: String *cdrecordInq(const char *dev); private: void cdrecordHelp() { setConfig(CDRECORDWANTSTAO, "false"); Process p(getCdrecordPath()); p.addArg("-help"); p.start(); while (1) { const char *lineStr = p.readLine(); if (lineStr == NULL) break; String line = lineStr; if (line.indexOf("gracetime=#") >= 0) setConfig("gracetime", "2"); else if (line.indexOf("-tao") >= 0) setConfig(CDRECORDWANTSTAO, "true"); } } private: int cdrecordScanbus(); private: void cdrecordVersion(); private: void checkCopy(const char *bin) { String copy = bin; copy = copy + "InternalPath"; copy = getActiveValue(copy.getBytes()); FILE *in1 = fopen(copy.getBytes(), "r"); if (in1 == NULL) throw new Exception(S.e // + "Cannot read the copy of " + bin + " (" + copy + ")", + "Cannot read the required tool " + copy, S.e + "/config/error/" + bin + ".html"); /* String orig = bin; orig = orig + "Path"; orig = getActiveValue(orig.getBytes()); if (orig.length() == 0) { fclose(in1); return; } FILE *in2 = fopen(orig.getBytes(), "r"); if (in2 == NULL) { fclose(in1); String url = "/config/error/"; url = url + bin + ".html"; setWarning(80, S.e + "Cannot read " + bin + " (" + orig + ")", url.getBytes()); return; } boolean differ = false; while (true) { int a = fgetc(in1); if (a == EOF) { differ = fgetc(in2) != EOF; break; } int b = fgetc(in2); differ = a != b; if (differ) break; } if (differ) { String url = "/config/error/"; url = url + bin + ".html"; setWarning(80, S.e + "The copy of " + bin + " is not up to date", url.getBytes()); } fclose(in2); */ fclose(in1); } private: void checkExecAsRoot(const char *fileName); private: void checkFile(const char *name, String &path); private: void checkForResMgrPatch(const char *bin) { String copy = getActiveBinPath(bin); log.put(3, S.e + "Config::checkForResMgrPatch " + copy); FILE *in = fopen(copy.getBytes(), "r"); if (in == NULL) throw new Exception(S.e + "Cannot read " + bin + " (" + copy + ")", S.e + "/config/error/" + bin + ".html"); StringBuffer strBuf = new StringBuffer(256 * 1024, 128 * 1024); while (true) { int a = fgetc(in); if (a == EOF) break; strBuf.append((char)a); } fclose(in); if (strBuf.indexOf("resmgr") >= 0) throw new Exception(S.e + bin + " with resmgr patch", S.e + "/config/error/resmgrPatch.html"); } private: void checkRawDevices() { if (getActiveBoolean(SIMULATION)) return; for (int i = 0; i < numOfWriters; i++) { if (!File(writers[i].rawDevice).exists()) throw (new Exception(S.e + "The raw device " + writers[i].rawDevice + " does not exist!")); prepareDevice(i); if (access(writers[i].rawDevice.getBytes(), R_OK | W_OK) == -1) { throw new Exception(S.e + "Cannot read and/or write the raw device " + writers[i].rawDevice + "!", S.e + "Try\n" + createHTMLTable(S.e + "$ chown " + user + " " + writers[i].rawDevice + "\n$ chmod u+rw" + " " + writers[i].rawDevice)); } } } private: String *createCheckbox(Device &device, const char *medium); private: String *createSpeedbox(Device &device, const char *medium); protected: String *createConfigLine(int index) { String value = items[index]->config.toString(); if (value.equals("false")) value = "off"; else if (value.equals("true")) value = "on"; String str = ""; str = str + items[index]->key + "=" + value; return new String(str); } public: static String *createHTMLTable(String &str) { String *tab = new String( "" "
");
		tab = &(*tab + str + "
"); String::free(&str); return tab; } public: bool deviceSupports(int no, String &medium, int speed) { String m = medium; if (m.length() == 0) m = "CD-R"; if (speed == 0) speed = 1; if (m.equals("CD") || m.equals("DVD")) if (!isReadOnlyDevice(no)) for (int i = 0; i < numOfWriters; i++) if (isReadOnlyDevice(i)) // use read only device instead return false; return (m.equals("CD") && writers[no].CD >= speed) || (m.equals("CD-R") && writers[no].CDR >= speed) || (m.equals("CD-RW") && writers[no].CDRW >= speed) || (m.equals("DVD") && writers[no].DVD >= speed) || (m.equals("DVD-R") && writers[no].DVDMinusR >= speed) || (m.equals("DVD-RW") && writers[no].DVDMinusRW >= speed) || (m.equals("DVD+R") && writers[no].DVDPlusR >= speed) || (m.equals("DVD+RW") && writers[no].DVDPlusRW >= speed) || (m.equals("DVD-RAM") && writers[no].DVDRAM >= speed) || (m.equals(DVDPlusR_DL) && writers[no].DVDPlusR_DL >= speed); } private: void evaluate( int lineNo, const char *str1, const char *str2); public: boolean getAccounts() { return accounts; } const char *getAccountsFile() { return accountsFile.getBytes(); } public: const char *getActiveBinPath(const char *bin) { String str = bin; if (str.equals("mkisofs") || str.equals("readcd")) str = str + "Internal"; else if (getActiveBoolean(SIMULATION)) { String str2 = str; str2 = str2 + "SimulationPath"; if (search(str2.getBytes()) >= 0) str = str + "Simulation"; } else if (str.equals("cdrdao") || str.equals("cdrecord")) str = str + "Internal"; str = str + "Path"; return getActiveValue(str.getBytes()).getBytes(); } boolean getActiveBoolean(const char *key) { int index = indexOf(key); Object &object = items[index]->active; if (!object.instanceof("Boolean")) throw new Exception("invalid item in getActiveBoolean"); return ((Boolean &)object).booleanValue(); } int getActiveInt(const char *key) { int index = indexOf(key); Object &object = items[index]->active; if (!object.instanceof("Integer")) throw new Exception("invalid item in getActiveInt"); return ((Integer &)object).intValue(); } String &getActiveValue(const char *key) { int index = indexOf(key); Object &object = items[index]->active; if (!object.instanceof("String")) throw new Exception("invalid item in getActiveValue"); return (String &)object; } boolean getAnonymousUse() { return getActiveBoolean("anonymousUse"); } boolean getBurnFree() { return getActiveBoolean("useBurnFree"); } boolean getCdrdao() { return getActiveBoolean("cdrdao"); } const char *getCdrdaoArgs() { return getActiveValue("cdrdaoArgs").getBytes(); } const char *getCdrdaoPath() { return getActiveBinPath("cdrdao"); } const char *getCdrecordArgs() { return getActiveValue("cdrecordArgs").getBytes(); } const char *getCdrecordPath() { return getActiveBinPath("cdrecord"); } const char *getConfigDir() { return configDir.getBytes(); } boolean getCopySupport() { return getActiveBoolean("copySupport") && hasReader; } const String &getDebug() { return debugCache; } const char *getDefaultFilesDir() { return getActiveValue("defaultFilesDir").getBytes(); } public: boolean getEditorBoolean(String &key) { int index = indexOf(key.getBytes()); Object &object = items[index]->editor; if (!object.instanceof("Boolean")) throw new Exception("invalid item in getEditorBoolean"); if (key.equalsIgnoreCase("useUTF8")) { log.put(5, S.e + "mkisofsWithUTF8=" + mkisofsWithUTF8); return mkisofsWithUTF8; } return ((Boolean &)object).booleanValue(); } public: String *getEditorValue(String &key) { int index = indexOf(key.getBytes()); return items[index]->editor.toString(); } String *getError(const char *key) { int index = indexOf(key); return new String(items[index]->error); } int getDefaultSpeed() { return getActiveInt(DEFAULTSPEED); } String &getDefaultValue(const char *key) { int index = indexOf(key); Object &object = items[index]->defaultValue; if (!object.instanceof("String")) throw new Exception("invalid item in getDefaultValue"); return (String &)object; } boolean getEjectOnDone() { return getActiveBoolean("ejectOnDone"); } boolean getEjectOnReservation() { return getActiveBoolean("ejectOnReservation"); } const char *getEMailOnError() { return eMailOnError; } boolean getExportArchive() { return getActiveBoolean("exportArchive"); } String &getFirstError() { return firstError; } String &getFirstErrorHelp() { return firstErrorHelp; } String *getFirstErrorURL() { if (firstErrorHelp.startsWith("/") || firstErrorHelp.startsWith("http://")) return new String(firstErrorHelp); return new String("/config/error/help.html"); } int getFirstErrorPriority() { return firstErrorPriority; } int getGid() { return gid; } const char *getGroup() { return group; } const char *getHttpDir() { return getActiveValue("HttpDir").getBytes(); } const char *getImageFile(int writerNo) { return writers[writerNo].imageFile.getBytes(); } boolean getImageOnTheFly() { return imageOnTheFlyCache; } const char *getInfoFile() { return infoFile.getBytes(); } const char *getInsertCD_MsgFile() { return insertCD_MsgFile.getBytes(); } int getLaserOn() { return laserOn; } const char *getLogDir() { return getActiveValue("logDir").getBytes(); } String &getLogoURL() { return getActiveValue("logoURL"); } int getLogLevel() { return logLevelCache; } int getMaxCopies() { return maxCopies; } int getMaxMBytesInSpoolDir() { return maxMBytesInSpoolDirCache; } int getMaxMBytesPerSession() { return maxMBytesPerSessionCache; } int getMaxOpenConnections() { return getActiveInt("maxOpenConnections"); } int getMaxOpenSessions() { return getActiveInt("maxOpenSessions"); } int getMaxSpeed() { return maxSpeed; } public: int getMaxSpeed(int no, const char *medium) { String str = writers[no].id; str = str + ".no"; int externalNo = getActiveInt(str.getBytes()); str = writers[no].id; str = str + "." + medium; try { int speed = getActiveInt(str.getBytes()); if (externalNo < 1) speed = 0; if (strcmp(medium, "DVD") == 0) speed *= 9; return speed; } catch (Exception *e) { delete e; add(str.getBytes(), 0); return 0; } } public: String *getMimeType(File &file); public: const char *getMkisofsArgs() { return getActiveValue("mkisofsArgs").getBytes(); } const char *getMkisofsPath() { return getActiveBinPath("mkisofs"); } boolean getMkisofsWithUTF8() { return mkisofsWithUTF8; } private: String *getNameOfCopy(File &binary) { File copy = new File(binDir, binary.getName()); return new String(copy.getPath()); } public: int getNumOfWriters() { return numOfWriters; } const char *getOrganization() { return organization; } unsigned short int getPortNo() { return getActiveInt("portNo"); } const char *getProjectsDir() { return getActiveValue("projectsDir").getBytes(); } private: String *getRawDevice(String &dev, int no); public: const char *getReadcdPath() { return getActiveBinPath("readcd"); } public: boolean getRemoveSessions() { return removeSessions; } int getReservedMBytes() { return reservedMBytesCache; } boolean getShowUsers() { return getActiveBoolean("showUsers"); } const char *getSpoolDir() { return getActiveValue("spoolDir").getBytes(); } unsigned short int getStatusPortNo() { return statusPortNo; } public: boolean getSupportForm() { return supportFormCache; } public: String *getSupportedMedia() { const char *ts[] = { "DVD-R, ", "DVD-RW, ", "DVD+R, ", "DVD+RW, ", "DVD-RAM, ", "DVD+R_DL, ", NULL }; String media = ""; for (int i = 0; i < numOfWriters; i++) { if (writers[i].DVDMinusR && media.indexOf(ts[0]) < 0) media = media + ts[0]; if (writers[i].DVDMinusRW && media.indexOf(ts[1]) < 0) media = media + ts[1]; if (writers[i].DVDPlusR && media.indexOf(ts[2]) < 0) media = media + ts[2]; if (writers[i].DVDPlusRW && media.indexOf(ts[3]) < 0) media = media + ts[3]; if (writers[i].DVDRAM && media.indexOf(ts[4]) < 0) media = media + ts[4]; if (writers[i].DVDPlusR_DL && media.indexOf(ts[5]) < 0) media = media + ts[5]; } if (media.length() > 2) media = media.substring(0, media.length() - 2); log.put(5, S.e + "getSupportedMedia(): <" + media + ">"); return new String(media); } int getUid() { return uid; } const char *getUser() { return user; } int getVerify() { return getActiveInt("verify"); } const char *getVerifyPath() { return getActiveBinPath("verify"); } int getWaitForCD() { return getActiveInt("waitForCD"); } const char *getWriter(int no) { return writers[no].dev.getBytes(); } int getWriterExternalNo(int writerNo) { return writers[writerNo].no; } String &getWriterInfo(int writerNo) { return writers[writerNo].info; } const char *getWriterRawDevice(int writerNo) { return writers[writerNo].rawDevice.getBytes(); } String *getWriterShortInfo(int writerNo) { String str = ""; str = str + writers[writerNo].no + " " + writers[writerNo].info; if (str.length() > 27) str = str.substring(0, 27); return new String(str); } String &getWriterSupports(int writerNo) { return writers[writerNo].supports; } public: String *getWritersForm(); public: int hasCdrecordDevATA() { Process p(getCdrecordPath()); p.addArg("dev=help"); p.start(); boolean hasATA = false; while (1) { const char *lineStr = p.readLine(); if (lineStr == NULL) break; String line = lineStr; if (line.indexOf("ATA:") >= 0) hasATA = true; } return hasATA; } public: int hasDVDplusRWSupport(int writerNo) { return writers[writerNo].DVDMinusR || writers[writerNo].DVDMinusRW || writers[writerNo].DVDPlusR || writers[writerNo].DVDPlusRW; } private: int indexOf(const char *key) { int index = search(key); if (index < 0) throw new Exception(S.e + key + " does not exist"); return index; } private: void initDevices(); public: int isDefault(const char *key) { int index = search(key); if (index < 0) throw new Exception(S.e + key + " does not exist"); return items[index]->isDefault(); } private: bool isMounted(const String &rawDevice); public: bool isReadOnlyDevice(int i) { return writers[i].isReadOnlyDevice(); } public: void load(String &aFileName); private: void load2(String &aFileName); public: static void parseLine( const char *lineStr, char *str1, char *str2); private: String *path(const char *env, const char *configure); private: String *path( const char *env, const char *configure, String command); public: void prepareDevice(int n) { prepareDevice(writers[n].rawDevice); } private: void prepareDevice(String &rawDevice) { if (getActiveBoolean(SIMULATION)) return; if (!File(rawDevice).exists()) { log.put(1, S.e + "FIXME: " + rawDevice + " does not exist"); return; } if (access(rawDevice.getBytes(), R_OK | W_OK) != 0) log.put(3, S.e + "Owner of " + rawDevice + " changed"); Process p(getActiveValue("rootGatePath")); p.addArg("prepareDevice"); p.addArg(S.e + rawDevice); p.addArg(S.e + uid); p.run(); } private: void preparePath(const char *command, int mode = 0) { if (!getActiveBoolean(SIMULATION)) if ((mode & PATH_COPY) > 0) checkCopy(command); String str = getActiveBinPath(command); File f = new File(str); if (!f.exists()) { String url = ""; if (str.indexOf("sox") > 0) { url = url + "/config/error/" + command + ".html"; } else url = url + "/config/tools.html#" + command; throw new Exception(S.e + "The tool " + command + " (" + str + ") does not exist", S.e + url); } if (!setuid) mode &= ~PATH_COPY; if (!getActiveBoolean(SIMULATION)) if ((mode & (PATH_COPY | PATH_ROOT)) > 0) checkExecAsRoot(str.getBytes()); log.put(2, S.e + command + " = " + str); } private: void readConfigFile(); private: void readConfigFile(String &name); private: void readConfigFile(String &name, FILE *inStream); private: String *readPassword(); public: static String *readValue(String file) { int fd = open(file.getBytes(), O_RDONLY); if (fd == -1) return new String(""); char buf[256]; int n = read(fd, buf, sizeof buf - 1); close(fd); if (n < 0) return new String(""); buf[n] = 0; return new String(buf); } public: void reload(); public: void resetEditor() { for (int i = 0; i < numOfItems; i++) { items[i]->resetEditor(); items[i]->error = ""; } } private: void resetItems() { for (int i = 0; i < numOfItems; i++) { items[i]->config = items[i]->defaultValue; items[i]->active = items[i]->config; items[i]->lineInConfig = -1; } resetEditor(); } public: void resetWarning() { if (firstErrorPriority < 100) { firstError = ""; firstErrorHelp = ""; firstErrorPriority = 0; } } public: bool runsOnLinux26() { bool linux26 = false; FILE *in = fopen("/proc/version", "r"); if (in == NULL) return false; char str[1024]; String line = fgets(str, sizeof str - 1, in); fclose(in); return line.startsWith("Linux version 2.6."); } private: int search(const char *key) { return search(key, 0, numOfItems - 1); } private: int search(const char *key, int a, int b) { if (a > b) return -1; int m = (a + b) / 2; int diff = items[m]->key.compareToIgnoreCase(key); if (diff > 0) return search(key, a, m - 1); else if (diff < 0) return search(key, m + 1, b); else return m; } private: void setConfig(const char *key, const char *value) { int index = indexOf(key); items[index]->setConfig(value); } private: void setDefault(String &device, const char *type, int speed) { String str = device; str = str + "." + type; int index = search(str.getBytes()); if (index < 0) add(str.getBytes(), speed); else items[index]->setDefault(speed); } public: void setEditor(const char *key, String &value) { int index = indexOf(key); items[index]->setEditor(value); } private: void setError(const char *key, const char *error) { int index = indexOf(key); items[index]->error = error; } public: void setPermissions(); public: void setResult(const char *key, const char *result) { int index = indexOf(key); items[index]->error = result; } public: void setWarning( int priority, String &warning, const char *help) { pthread_mutex_lock(&mutex); try { if (priority > firstErrorPriority) { firstError = warning; firstErrorHelp = help; firstErrorPriority = priority; String::free(&warning); } } catch (Exception *e) { pthread_mutex_unlock(&mutex); throw e; } pthread_mutex_unlock(&mutex); } private: void testGrowisofs(); private: void testMkisofs(); private: void testMpg123(); private: void testOgg123(); private: void testSox(); static int toGid(const char *groupName); static String *toGroup(int gid); public: String &toHTML(); private: static const char *toString(boolean b); static int toUid(const char *userName); static String *toUser(int uid); private: void updateConfig() { for (int i = 0; i < numOfItems; i++) items[i]->updateConfig(); } public: void updateConfigFile(); public: void updateEditor(String &str); private: void write(); void write(const char *str); void write(const char *str1, const char *str2); void write(const char *str, int number); void writeConfigFile(); void writeInfoFile(); void writeInsertCD_MsgFile(); private: void writePassword(String &password); }; extern Config config; #endif