/* * @(#)Server.cpp * * 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, 21.04.1999 */ #include #include #ifndef FreeBSD #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Accounts.h" #include "Cdrdao.h" #include "Cdrecord.h" #include "Hints.h" #include "InputStream.h" #include "Support.h" #include "Process.h" #include "Version.h" #include "Log.h" #include "MP3.h" #include "Config.h" #include "Ogg.h" #include "Queue.h" #include "Session.h" #include "Server.h" #include "Socket.h" #include "Store.h" #include "Status.h" #include "StringTokenizer.h" #include "Wave.h" #include "WriterQueues.h" #ifdef PAM #define CDWserver #include "PAM.h" #endif /** * Create a new server process. * * @version 20050928 * @author Jörg P. M. Haeger */ Server::Server(Socket &socket): socket(socket), inStream(socket.getInputStream2()) { time(&connectionT0); log.put(2, S.e + "Server::Server started " + timeToString(connectionT0)); pthread_mutex_lock(&activateConfigMutex); activeServersNum++; serverNo = nextServerNo++; pthread_mutex_unlock(&activateConfigMutex); // inStream = NULL; logFilesNum = 0; // states bytesReceived = 0; openCommands = 0; isOffline = 0; lineStrPending = 0; clientLocale = "en"; clientSoftware = unknown; clientSystemName = clientSystemVersion = unknown; clientUserID = clientUserName = unknown; userID = userPassword = unknown; ejectBeforeVerify = true; // modes modeShowCdrecord = 0; modeShowMkisofs = 0; session = NULL; tmpSessionID = NULL; project = NULL; quit = 0; if (!socket.isConnected()) return; // try to determine the requesting host clientIP = unknown; struct sockaddr_in peerName; socklen_t peerNameLen = sizeof peerName; if (getpeername(socket.getFD(), (struct sockaddr *)&peerName, &peerNameLen) == 0) if (peerName.sin_family == AF_INET) { clientIP = inet_ntoa(peerName.sin_addr); log.put(2, S.e + "peerName <" + clientIP + "> port = " + peerName.sin_port); } // set the socket to SO_KEEPALIVE int on = 1; if (setsockopt(socket.getFD(), SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof on) < 0) log.put(1, S.e + "setsockopt SO_KEEPALIVE: " + strerror(errno)); // inStream = socket.getInputStream2(); } const char Server::errorInCommand[] = "error in command", Server::hint[] = "HINT", Server::hintPercentDone[] = "percentDone", Server::info[] = "INFO", Server::unknown[] = "unknown"; pthread_mutex_t Server::activateConfigMutex = PTHREAD_MUTEX_INITIALIZER; bool Server::activateConfigPending = false; int Server::activeServersNum = 0; String Server::features = "CD-R, CD-RW"; String Server::license = "GNU General Public License"; int Server::maxServers = 1; int Server::maxUsers = 10000; String Server::serialNo = "-"; String Server::validity = "Unlimited"; pthread_mutex_t Server::licenseMutex = PTHREAD_MUTEX_INITIALIZER; int Server::nextServerNo = 1; WriterQueues *Server::writerQueue = NULL; Writer *Server::writers = new Writer[maxNumOfWriters]; Server::~Server() { if (socket.isConnected()) logConnection(); if (session != NULL) { delete session; session = NULL; } if (tmpSessionID != NULL) { store->removeSession(tmpSessionID); delete[] tmpSessionID; tmpSessionID = NULL; } if (project != NULL) { fclose(project); project = NULL; } pthread_mutex_lock(&activateConfigMutex); activeServersNum--; pthread_mutex_unlock(&activateConfigMutex); if (activateConfigPending) Server::activateConfig(); log.put(2, S.e + "Server::~Server terminated " + timeToString()); } void Server::activateConfig() { pthread_mutex_lock(&activateConfigMutex); if (activeServersNum == 0) { config.reload(); resetStatic(); activateConfigPending = false; } else activateConfigPending = true; pthread_mutex_unlock(&activateConfigMutex); } void Server::addArgs(Process &p, const char *args) { p.addArgs(args); } void Server::blank(char *cmdStr) { int hasWriter = 0; try { writerQueue->add(serverNo, options.medium, options.speed); reserveDevice(serverNo); hasWriter = 1; strcat(cmdStr, " "); const char *modeStr; if (strstr(cmdStr, " all ") != NULL) modeStr = "all"; else if (strstr(cmdStr, " fast ") != NULL) modeStr = "fast"; else if (strstr(cmdStr, " session ") != NULL) modeStr = "session"; else modeStr = "help"; waitForDisc(); readATIP(); readTOC(); toClient(hint, hintCDrecordStarted); run_cdrecordBlank(modeStr); writer().percent = 100; try { readTOC(); } catch (Exception *e2) { delete e2; } if (config.getEjectOnDone()) eject(); log.put("cdrecord success"); successToClient("cdrecord success"); writer().state = idle; writerQueue->remove(serverNo); } catch (Exception *e) { if (hasWriter) { if (!noDisc() && config.getEjectOnDone()) eject(); writer().state = idle; } writerQueue->remove(serverNo); throw e; } } void Server::burnCopy() { int hasReader = 0, hasWriter = 0; try { String CD = "CD"; writerQueue->add(-serverNo, CD); reserveDevice(-serverNo); readerNo = writerNo(); writers[readerNo].thread = NULL; hasReader = 1; ejectReader(); writerQueue->add(serverNo, options.medium, options.speed); reserveDevice(serverNo); hasWriter = 1; time(&writer().burnT0); toClient(info, S.e + "Please insert the master CD into the drive No. " + config.getWriterExternalNo(readerNo) + " (" + config.getWriterInfo(readerNo) + ")!"); for (int i = 0; i < options.copies; i++) { String command = "cdrecord", type = "C"; waitForDisc(); try { if (options.blankTOC) { toClient(hint, hintBlankingTOC); run_cdrecordBlank("fast"); } toClient(hint, hintCDrecordStarted); writers[readerNo].state = reading; if (config.getCdrdao()) { command = "cdrdao"; run_cdrdaoCopy(); if (options.verify && CDState().firstTrackIsData) verify(); } else { run_cdrecordCopy(); if (options.verify) verify(); } } catch (Exception *e) { try { readTOC(); } catch (Exception *e2) { delete e2; } logSession(type.getBytes(), e->getMessage()); throw e; } logSession(type.getBytes(), "OK"); eject(); command = command + " success"; if (options.copies > 1) log.put(3, S.e + command + " (copy " + (i + 1) + ")"); else log.put(3, S.e + command); } ejectReader(); successToClient("cdrecord success"); writer().state = idle; writerQueue->remove(serverNo); writers[readerNo].state = idle; writerQueue->remove(-serverNo); } catch (Exception *e) { if (hasWriter) { if (!noDisc()) eject(); writer().state = idle; } if (hasReader) { ejectReader(); writerQueue->remove(serverNo); writers[readerNo].state = idle; } writerQueue->remove(-serverNo); throw e; } } void Server::burnSession() { // end of transfer time(&sessionT1); copiesReady = 0; copiesToDo = options.copies; if (session->getFirstTrack() == NULL) throw new Exception("empty session"); burnSession2(100 * serverNo); successToClient("cdrecord success"); } void Server::burnSession2(int threadID) { writerQueue->add(threadID, options.medium, options.speed); Exception *exception = NULL; try { reserveDevice(threadID); burnSession3(); } catch (Exception *e) { exception = e; } // finally { writerQueue->remove(threadID); // } if (exception != NULL) throw exception; } void Server::burnSession3() { time(&writer().burnT0); Exception *exception = NULL; try { burnSession4(); } catch (Exception *e) { exception = e; } // finally { if (!noDisc() && config.getEjectOnDone()) eject(); writer().state = idle; resetImageFile(); // } if (exception != NULL) throw exception; } void Server::burnSession4() { int makeImage = session->getFirstTrack()->isDirectory(); String command = "", type = "?"; if (makeImage) type = "S"; else if (session->getFirstTrack()->getName().endsWith(".wav")) type = "A"; else type = "I"; for (; copiesToDo > 0; copiesToDo--) { waitForDisc(); try { command = "cdrecord"; // readATIP(); if (options.blankTOC) { toClient(hint, hintBlankingTOC); try { run_cdrecordBlank("fast"); } catch (Exception *e) { if (CDState().isRW) throw e; else delete e; } } if (makeImage) { toClient(hint, hintMakingImage); run_mkisofs(); toClient(hint, hintImageReady); } toClient(hint, hintCDrecordStarted); run_cdrecord(); if (options.verify) verify(); } catch (Exception *e) { try { readTOC(); } catch (Exception *e2) { delete e2; } logSession(type.getBytes(), e->getMessage()); log.error(S.e + e->getMessage().getBytes()); throw e; } logSession(type.getBytes(), "OK"); // try { readTOC(); } catch (Exception *e2) { delete e2; } if (config.getEjectOnDone()) eject(); command = command + " success"; if (options.copies > 1) toClient(101, S.e + command + " (copy " + (++copiesReady) + ")", 2); else log.put(3, S.e + command); } } void Server::checkFileName(const char *fileName) { int i = 0; while (1) { while (fileName[i] == '/') i++; if (fileName[i] == 0) break; if (fileName[i] == '.' && fileName[i + 1] == '.' && (fileName[i + 2] == 0 || fileName[i + 2] == '/')) throw new Exception("file name contains /../"); while (fileName[i] != 0 && fileName[i] != '/') i++; } } void Server::checkLoggedIn() { if (userPassword == unknown) throw new Exception("Not logged in."); } void Server::checkOpenSession() { if (session == NULL) throw new Exception("no open session"); } String *Server::dateToShortString() { time_t t; time(&t); struct tm *tm = localtime(&t); String *str = new String(); *str = *str + (1900 + tm->tm_year) + (tm->tm_mon + 1 < 10? "0": "") + (tm->tm_mon + 1) + (tm->tm_mday < 10? "0": "") + tm->tm_mday; return str; } void Server::eject2() { try { Cdrecord p(writerNo()); p.addArg("-inq"); p.addArg("-V"); p.run(); if (p.isTrayOpen()) return; } catch (Exception *e) { delete e; } try { Cdrecord p(writerNo()); p.addArg("-eject"); p.run(); if (p.getResult() != 0) throw new Exception(S.e + "Cannot open tray" + " http://JoergHaeger.de/help/CannotOpenTray.html"); } catch (Exception *e) { throw e; } } void Server::ejectReader() { try { Cdrecord p(readerNo); p.addArg("-eject"); p.run(); } catch (Exception *e) { throw e; } } void Server::errorToClient(const char *format, ...) { if (isOffline) return; if (openCommands == 0) { log.put("error: openCommands = %d", openCommands); return; } char str[8 * 1024]; va_list ap; va_start(ap, format); vsnprintf(str, sizeof str - 1, format, ap); va_end(ap); String line = ""; line = line + "ERR " + str + "\n"; toClientWriteLine(line); openCommands--; } void Server::errorToClient(String &str) { errorToClient("%s", str.getBytes()); String::free(&str); } void Server::executeCmd() { openCommands++; char cmd[2 * sizeof lineStr], arg1[2 * sizeof lineStr], arg2[2 * sizeof lineStr], arg3[2 * sizeof lineStr], arg4[2 * sizeof lineStr], arg5[2 * sizeof lineStr]; cmd[0] = arg1[0] = arg2[0] = arg3[0] = arg4[0] = arg5[0] = 0; int strsNum = sscanf(lineStr, "%s %s %s %s %s %s", cmd, arg1, arg2, arg3, arg4, arg5); removeEscChars(arg1); removeEscChars(arg2); removeEscChars(arg3); removeEscChars(arg4); removeEscChars(arg5); int argsNum = strsNum - 1; // write lineStr to the log if (strcasecmp(cmd, "changePassword") == 0 || strcasecmp(cmd, "login") == 0) { // don't write passwords to the log String msg = cmd; char *passwd = arg1; if (msg.equalsIgnoreCase("login")) { msg = msg + " " + arg1; passwd = arg2; } msg = msg + " xxxx"; #ifdef PAM if (!config.getActiveBoolean("PAM")) #endif strcpy(passwd, &crypt(passwd, "JH")[2]); log.put(2, S.e + msg); } else if (strsNum > 0 && (strncasecmp(cmd, "putFile", 7) == 0 || strcasecmp(cmd, "mkdir") == 0)) log.putv("command: <%s>", lineStr); else log.put("command: <%s>", lineStr); // anonymous mode commands if (userPassword == unknown && !config.getAnonymousUse() && strcasecmp(cmd, "client") != 0 && strcasecmp(cmd, "login") != 0 && strcasecmp(cmd, "newAccount") != 0 && strcasecmp(cmd, "setProtocol") != 0 && strcasecmp(cmd, "system") != 0 && strcasecmp(cmd, "user") != 0 && strcasecmp(cmd, "vm") != 0) throw new Exception("Anonymous use is not permitted."); // interpret the command if (project != NULL) { // save project mode commands if (strcasecmp(cmd, "add") == 0 || strcasecmp(cmd, "boot") == 0 || strcasecmp(cmd, "medium") == 0 || strcasecmp(cmd, "mkdir") == 0 || strcasecmp(cmd, "mv") == 0 || strcasecmp(cmd, "rm") == 0 || strcasecmp(cmd, "type") == 0) { fprintf(project, "%s\n", lineStr); successToClient(""); return; } else if (strcasecmp(cmd, "list") == 0) ; else { fclose(project); project = NULL; } } if (strlen(lineStr) == 0) throw new Exception("empty command"); else if (strcasecmp(cmd, "blank") == 0) blank(lineStr); else if (strcasecmp(cmd, "burnCopy") == 0) { if (config.getCopySupport()) burnCopy(); else throw new Exception("not supported"); } else if (strcasecmp(cmd, "burnSession") == 0) { checkOpenSession(); try { // compatibility with protocol version 1 if (strstr(lineStr, "multi") != NULL) options.closeCD = 0; if (strstr(lineStr, "offline") != NULL) options.burnOffline = 1; // extension for special environments if (argsNum == 1 && strcasecmp(arg1, "--reserveWriterOffline") == 0) { successToClient("reserving and burning offline"); options.burnOffline = isOffline = 1; } burnSession(); delete session; session = NULL; } catch (Exception *e) { delete session; session = NULL; throw e; } } else if (strcasecmp(cmd, "changePassword") == 0) { if (argsNum != 1) throw new Exception(errorInCommand); checkLoggedIn(); accounts->changePassword(userID, arg1); successToClient(""); } else if (strcasecmp(cmd, "client") == 0) { if (argsNum == 2) { String str = arg1; if (str.startsWith("webCDcreator")) { clientSoftware = arg1; clientSoftware = clientSoftware + arg2; } else if (str.equals("locale")) clientLocale = arg2; } successToClient(""); } else if (strcasecmp(cmd, "closeSession") == 0) { // checkOpenSession(); if (session != NULL) delete session; session = NULL; successToClient(""); } else if (strcasecmp(cmd, "getSessionInfo") == 0) { if (strsNum == 2) { int files, tracks, size_KB; store->getInfo(userID, userPassword, arg1, files, tracks, size_KB); toClient(hint, "files %d", files); toClient(hint, "tracks %d", tracks); successToClient(""); } else errorToClient("in command %s", cmd); } else if (strcasecmp(cmd, "linkFile") == 0) { if (argsNum < 2 || argsNum > 3) throw new Exception(errorInCommand); checkOpenSession(); checkFileName(arg1); checkFileName(arg2); File file = new File(config.getActiveValue("exportDir"), arg1); if (!file.exists()) { if (file.getName().equals("MD5Verify.jar")) { file = new File(config.getDefaultValue("exportDir"), arg1); if (!file.exists()) file = new File( config.getDefaultValue("binDir"), "MD5Verify.jar"); if (!file.exists()) throw new Exception(S.e + "The file " + file.getPath() + " does not exist on the server"); } else throw new Exception(S.e + "The file " + arg1 + " does not exist on the server"); } session->linkFile(file, arg2); successToClient("file linked"); } else if (strcasecmp(cmd, "linkTrack") == 0) { if (argsNum != 2) throw new Exception(errorInCommand); checkOpenSession(); checkFileName(arg1); File track = new File(config.getActiveValue("exportDir"), arg1); if (!track.exists()) throw new Exception(S.e + "Track " + arg1 + " does not exist on the server"); session->linkTrack(track, arg2); successToClient("track linked"); } else if (strcasecmp(cmd, "list") == 0) { char *dirName = arg1; if (argsNum != 1) throw new Exception(errorInCommand); checkFileName(dirName); File f = new File(config.getActiveValue("exportDir"), dirName); DIR *dir = opendir(f.getPath().getBytes()); if (dir == NULL) throw new Exception("not a directory"); struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { String name = dirEntry->d_name; if (name.equals(".") || name.equals("..")) continue; File f2(f, name.getBytes()); String type = ""; if (name.endsWith(".mp3") || name.endsWith(".MP3")) try { MP3 mp3(f2); type = type + " MP3 " + (int)mp3.getSeconds(); } catch (Exception *e) { delete e; } else if (name.endsWith(".ogg") || name.endsWith(".OGG")) try { Ogg ogg(f2); type = type + " OGG " + (int)ogg.getSeconds(); } catch (Exception *e) { delete e; } else if (name.endsWith(".wav") || name.endsWith(".WAV")) try { Wave wave(f2); type = type + " WAV " + (int)wave.getSeconds(); } catch (Exception *e) { delete e; } const char *name2 = spaceToEsc(name.getBytes()); toClient(hint, S.e + (f2.isDirectory()? "d": "f") + " " + f2.length() + " " + name2 + type.getBytes()); delete[] name2; } closedir(dir); successToClient("list done"); } else if (strcasecmp(cmd, "listProjects") == 0) { if (argsNum != 0) throw new Exception(errorInCommand); File dir0(config.getProjectsDir(), userID); DIR *dir = opendir(dir0.getPath().getBytes()); if (dir == NULL) throw new Exception(S.e + "Cannot open the directory " + dir0.getPath().getBytes()); struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { if (strcmp(dirEntry->d_name, ".") == 0 || strcmp(dirEntry->d_name, "..") == 0) continue; int files = 0, tracks = 0, size = 0; store->getInfo(userID, userPassword, dirEntry->d_name, files, tracks, size); const char *name = spaceToEsc(dirEntry->d_name); toClient(hint, "%s %d %d %d %d", name, files, tracks, size, File(dir0, dirEntry->d_name).lastModified()); delete[] name; } closedir(dir); successToClient(""); } else if (strcasecmp(cmd, "listSessions") == 0) { const int max = 100; const char *IDs[max]; int n = store->list(userID, userPassword, IDs, max); for (int i = 0; i < n; i++) { toClient(hint, "%s", IDs[i]); delete[] IDs[i]; } successToClient(""); } else if (strcasecmp(cmd, "loadProject") == 0) { if (argsNum != 1) throw new Exception(errorInCommand); checkLoggedIn(); if (project != NULL) { fclose(project); project = NULL; } char str[1024]; snprintf(str, sizeof str, "%s/%s/%s", config.getProjectsDir(), userID, arg1); project = fopen(str, "r"); if (project == NULL) throw new Exception(S.e + "Cannot open " + str); while (1) { if (fgets(str, sizeof str, project) == NULL) break; toClient(hint, "%s", str); } fclose(project); project = NULL; successToClient(""); } else if (strcasecmp(cmd, "login") == 0) { if (argsNum != 2) throw new Exception(errorInCommand); if (options.protocolVersion >= 3) { #ifdef PAM if (config.getActiveBoolean("PAM")) { PAM4CDWserver::check(arg1, arg2); strcpy(arg2, &crypt(arg2, "JH")[2]); } else #endif accounts->check(arg1, arg2); userID = strDup(arg1); userPassword = strDup(arg2); } successToClient("LOGIN completed"); } else if (strcasecmp(cmd, "mkdir") == 0) { char *dirName = arg1; int mode, time; if (argsNum != 3 || sscanf(arg2, "%o", &mode) != 1 || sscanf(arg3, "%d", &time) != 1) throw new Exception(errorInCommand); checkOpenSession(); checkFileName(dirName); session->makeDir(dirName, mode, time); successToClient("directory created"); } else if (strcasecmp(cmd, "newAccount") == 0) { if (argsNum != 2) throw new Exception(errorInCommand); if (config.getActiveBoolean("managedAccounts")) throw new Exception(S.e + "Ask the admin for an account!"); if (strcmp(arg1, "CDinfos") == 0 || strcmp(arg1, "archive") == 0 || strcmp(arg1, "bin") == 0 || strcmp(arg1, "config") == 0 || strcmp(arg1, "connects") == 0 || strcmp(arg1, "lineStr") == 0 || strcmp(arg1, "log") == 0 || strcmp(arg1, "projects") == 0 || strcmp(arg1, "root") == 0 || strcmp(arg1, "sessions") == 0 || strcmp(arg1, "vlog") == 0 || strstr(arg1, ".log") != NULL || strstr(arg1, ".vlog") != NULL) throw new Exception("reserved user name"); if (strstr(arg1, "-") != NULL || strstr(arg1, ":") != NULL || strstr(arg1, "/") != NULL || strstr(arg1, "\\") != NULL || strstr(arg1, "..") != NULL) throw new Exception("invalid user name"); char passwd[8 + 1]; time_t t; time(&t); srandom(t); for (int i = 0; i < sizeof passwd - 1; i++) passwd[i] = 'A' + random() % ('Z' - 'A' + 1); passwd[sizeof passwd - 1] = 0; accounts->add(arg1, &crypt(passwd, "JH")[2], arg2); char cmdStr[4 * 1024]; snprintf(cmdStr, sizeof cmdStr, "echo User: %s, Password: %s | mail -s %s %s", arg1, passwd, "\"webCDwriter Account\"", arg2); if (system(cmdStr) != 0) { accounts->remove(arg1); throw new Exception(S.e + "Sending an eMail to " + arg2 + " failed."); } successToClient("added"); } else if (strcasecmp(cmd, "newSession") == 0 || strcasecmp(cmd, "openSession") == 0) { if (argsNum > 1) throw new Exception(errorInCommand); if (argsNum == 0) snprintf(arg1, sizeof arg1, "%04d", serverNo); char userID2[strlen(userID) + 1 + 1]; if (userPassword == unknown) { sprintf(userID2, ":%s", userID); snprintf(arg1, sizeof arg1, "%04d", serverNo); } else strcpy(userID2, userID); if (session != NULL) { delete session; session = NULL; } session = store->createSession(this, userID2, "", arg1, strcasecmp(cmd, "newSession") == 0); // delete a different previous temporary session if (tmpSessionID != NULL && strcmp(tmpSessionID, session->getID()) != 0) { store->removeSession(tmpSessionID); delete[] tmpSessionID; tmpSessionID = NULL; } // store the ID of a temporary session if (userPassword == unknown || argsNum == 0) tmpSessionID = strDup(session->getID()); if (strcasecmp(cmd, "openSession") == 0) { toClient(hint, "files %d", session->getFilesNum()); toClient(hint, "tracks %d", session->getTracksNum()); } options.reset(); if (argsNum > 0) options.volumeID = arg1; successToClient(""); time(&sessionT0); } else if (strcasecmp(cmd, "noop") == 0) successToClient(""); else if (strcasecmp(cmd, "putFile") == 0) { char *fileName = arg1; int length, mode, time; if (argsNum != 4 || sscanf(arg2, "%d", &length) != 1 || sscanf(arg3, "%o", &mode) != 1 || sscanf(arg4, "%d", &time) != 1) throw new Exception(errorInCommand); bytesReceived += length; try { checkOpenSession(); checkFileName(fileName); } catch (Exception *e) { inStream.skip(length); throw e; } session->putFile(inStream, fileName, length, mode, time); successToClient("file stored"); } else if (strcasecmp(cmd, "putFileContinue") == 0) { int length; if (argsNum != 1 || sscanf(arg1, "%d", &length) != 1) throw new Exception(errorInCommand); bytesReceived += length; try { checkOpenSession(); } catch (Exception *e) { inStream.skip(length); throw e; } putFileContinue(inStream, length); } else if (strcasecmp(cmd, "putFileEnd") == 0) { int mode, time; if (argsNum != 2 || sscanf(arg1, "%o", &mode) != 1 || sscanf(arg2, "%d", &time) != 1) throw new Exception(errorInCommand); checkOpenSession(); putFileEnd(mode, time); successToClient("file closed"); } else if (strcasecmp(cmd, "putFileStart") == 0) { if (argsNum != 1 && argsNum != 2) throw new Exception(errorInCommand); const char *fileName = arg1; checkOpenSession(); checkFileName(fileName); putFileStart(fileName, arg2); successToClient("file created"); } else if (strcasecmp(cmd, "putTrack") == 0) { char *trackType = arg1; int length; if (argsNum != 2 || sscanf(arg2, "%d", &length) != 1) throw new Exception(errorInCommand); bytesReceived += length; try { checkOpenSession(); if (strcasecmp(trackType, "wav") == 0 || strcasecmp(trackType, "mp3") == 0 || strcasecmp(trackType, "ogg") == 0) if (!config.getActiveBoolean("audioCDSupport")) throw new Exception("no audio CD support"); if (strcasecmp(trackType, "mp3") == 0 && !config.getActiveBoolean("MP3decoding")) throw new Exception("no MP3 decoding"); if (strcasecmp(trackType, "ogg") == 0 && !config.getActiveBoolean("oggDecoding")) throw new Exception("no ogg decoding"); } catch (Exception *e) { inStream.skip(length); throw e; } if (strcmp(trackType, "continue") != 0) { session->newTrack(trackType); if (strcmp(trackType, "inf") == 0) options.CDText = 1; } session->putTrack(inStream, length); successToClient("file stored"); } else if (strcasecmp(cmd, "quit") == 0) { log.put("connection terminated by client"); successToClient(""); quit = 1; } else if (strcasecmp(cmd, "remove") == 0) { if (argsNum != 1) throw new Exception(errorInCommand); char *fileName = arg1; checkOpenSession(); checkFileName(fileName); session->remove(fileName); successToClient("file removed"); } else if (strcasecmp(cmd, "resetOptions") == 0) { options.reset(); successToClient(""); } else if (strcasecmp(cmd, "saveProject") == 0) { if (argsNum != 1) throw new Exception(errorInCommand); checkLoggedIn(); if (project != NULL) { fclose(project); project = NULL; } char str[1024]; snprintf(str, sizeof str, "%s/%s/%s", config.getProjectsDir(), userID, arg1); makeDirs(str); project = fopen(str, "w"); if (project == NULL) throw new Exception(S.e + "Cannot open " + str); log.put("project %s started", str); successToClient(""); } else if (strcasecmp(cmd, "setAcceptEmptyCD") == 0) { if (openCommands > 0) openCommands--; } else if (strcasecmp(cmd, "setAcceptKey") == 0) { if (strsNum != 2) throw new Exception("setAcceptKey "); if (strlen(arg1) > 32) arg1[32] = 0; options.acceptKey = arg1; successToClient(""); } else if (strcasecmp(cmd, "setAcceptKey2") == 0) { if (openCommands > 0) openCommands--; } else if (strcasecmp(cmd, "setAcceptMedium") == 0) { if (openCommands > 0) openCommands--; } else if (strcasecmp(cmd, "setAcceptWithoutUserID") == 0) { if (openCommands > 0) openCommands--; } else if (strcasecmp(cmd, "setBlankTOC") == 0) { options.blankTOC = 1; successToClient(""); } else if (strcasecmp(cmd, "setBootImage") == 0) { if (strsNum != 2) throw new Exception("setBootImage "); checkFileName(arg1); char *ptr = arg1; while (*ptr == '/') ptr++; options.bootImage = ptr; successToClient(""); } else if (strcasecmp(cmd, "setBurnOffline") == 0) { options.burnOffline = 1; successToClient(""); } else if (strcasecmp(cmd, "setCloseCD") == 0) { options.closeCD = 1; successToClient(""); } else if (strcasecmp(cmd, "setCopies") == 0) { if (strsNum == 2 && sscanf(arg1, "%d", &options.copies) == 1 && options.copies > 0 && options.copies <= config.getMaxCopies()) successToClient(""); else errorToClient("missing or invalid argument"); } else if (strcasecmp(cmd, "setDAO") == 0) { options.DAO = 1; successToClient(""); } else if (strcasecmp(cmd, "setDVDVideo") == 0) { options.DVDVideo = 1; successToClient(""); } else if (strcasecmp(cmd, "setFormat") == 0) { options.format = 1; successToClient(""); } else if (strcasecmp(cmd, "setHFS") == 0) { options.HFS = 1; successToClient(""); } else if (strcasecmp(cmd, "setHideContents") == 0) { options.hideContents = 1; successToClient(""); } else if (strcasecmp(cmd, "setJoliet") == 0) { options.joliet = 1; successToClient(""); } else if (strcasecmp(cmd, "setLaserOff") == 0) { options.laserOff = 1; successToClient(""); } else if (strcasecmp(cmd, "setMedium") == 0) { if (argsNum != 1) throw new Exception(S.e + "setMedium [CD-R | CD-RW" + " | DVD-RW | DVD-R | DVD+RW | DVD+R | DVD+R%20DL]"); String medium = arg1; if (medium.equals("DVD+R DL")) medium = DVDPlusR_DL; if (medium.equals("CD-R") || medium.equals("CD-RW")) ; else checkMediumSupport(medium.getBytes()); options.medium = medium; successToClient(""); } else if (strcasecmp(cmd, "setOverburn") == 0) { options.overburn = config.getActiveBoolean("overburnSupport"); successToClient(""); } else if (strcasecmp(cmd, "setProjectInfo") == 0) { if (argsNum != 4) throw new Exception(S.e + "setProjectInfo