/* * @(#)Session.cpp * * This file is part of webCDwriter - Network CD/DVD Writing. * * Copyright (C) 1999-2005 Jörg P. M. Haeger * * webCDwriter is free software. See CDWserver.cpp for details. * * Jörg Haeger, 05.06.1999 */ #include #include #include #include #include #include "Config.h" #include "Support.h" #include "Log.h" #include "MP3ToWAV.h" #include "OggToWAV.h" #include "Queue.h" #include "Server.h" #include "Session.h" #include "Store.h" #include "Wave.h" #include "WAVToWAV.h" const char CDWserverInfo[] = "CDWserver.info"; Session::Session(Server *server, const char *ID, const char *dir): currentFile("") { this->server = server; this->ID = strDup(ID); this->dir = new File(dir); filesNum = tracksNum = 0; sizeInBytes = 0; fileOutputStream = NULL; currentTrack = NULL; currentTrackNo = 0; linksNum = 0; audioToWAV = NULL; if (this->dir->exists()) readInfo(); else create(); } Session::~Session() { if (audioToWAV != NULL) closeAudioTrack(); if (fileOutputStream != NULL) putFileEnd(); writeInfo(); store->unregister(this); if (config.getRemoveSessions()) store->removeSession(ID); if (currentTrack != NULL) delete currentTrack; delete dir; delete[] ID; } void Session::checkContainsNoLink(const char *fileName) { if (linksNum == 0) return; if (currentTrack == NULL || !currentTrack->isDirectory()) return; File file = new File(*currentTrack); int i = 0; while (1) { while (fileName[i] == '/') i++; if (fileName[i] == 0) break; char str[strlen(fileName) + 1]; int j = 0; while (fileName[i] != 0 && fileName[i] != '/') str[j++] = fileName[i++]; str[j] = 0; file = new File(file, str); if (file.isLink()) throw new Exception("file name contains link"); } } void Session::closeAudioTrack() { log.debug("audioToWAV", "Session::closeAudioTrack()"); if (audioToWAV == NULL) { log.put(1, "Session::closeAudioTrack(): audioToWAV == NULL"); return; } audioToWAV->end(); FILE *outStream = fopen(currentTrack->getPath().getBytes(), "r+"); if (outStream != NULL) { fseek(outStream, 0, SEEK_END); try { while (1) { const char *buf; int n = audioToWAV->get(&buf); if (n == 0) break; copy(buf, outStream, n); } } catch (Exception *e) { delete e; } audioToWAV->updateHeader(outStream); fclose(outStream); } delete audioToWAV; audioToWAV = NULL; log.debug("audioToWAV", "Session::closeAudioTrack() end"); } void Session::copy(InputStream &inStream, FILE *outStream, int length) { try { copy2(inStream, outStream, length); } catch (Exception *e) { inStream.skip(length); throw e; } } void Session::copy(const char *buf, FILE *outStream, int length) { if (sizeToKBytes(sizeInBytes + length) > 1024 * config.getMaxMBytesPerSession()) throw new Exception("session too big"); int fd = fileno(outStream); if (fd == -1) throw new Exception("Session::copy: fd == -1"); sizeInBytes += length; store->write(buf, length, fd); int lengthMod1024 = length % 1024; if (lengthMod1024 > 0) sizeInBytes += 1024 - lengthMod1024; } void Session::copy2(InputStream &inStream, FILE *outStream, int &length) { if (sizeToKBytes(sizeInBytes + length) > 1024 * config.getMaxMBytesPerSession()) throw new Exception("session too big"); int fd = fileno(outStream); if (fd == -1) throw new Exception("Session::copy2: fd == -1"); while (length > 0) { char buf[16 * 1024]; int n = 0; const int bufSize = min(length, sizeof buf); while (true) { int nn = inStream.read(buf, n, bufSize - n); if (nn <= 0) throw new Exception("unexpected end of inStream"); length -= nn; n += nn; if (n == bufSize) break; } sizeInBytes += n; store->write(buf, n, fd); int nMod1024 = n % 1024; if (nMod1024 > 0) sizeInBytes += 1024 - nMod1024; } } void Session::countFile(const char *pathName) { struct stat statBuf; if (lstat(pathName, &statBuf) != 0) return; if (!S_ISDIR(statBuf.st_mode)) { if (!S_ISREG(statBuf.st_mode)) return; filesNum++; int64 size_KB = sizeToKBytes(statBuf.st_size); sizeInBytes += 1024 * size_KB; return; } DIR *dir = opendir(pathName); if (dir == NULL) return; struct dirent *dirEntry; while ((dirEntry = readdir(dir)) != NULL) { if (strcmp(dirEntry->d_name, ".") == 0 || strcmp(dirEntry->d_name, "..") == 0) continue; char tmpPathName[strlen(pathName) + 1 + strlen(dirEntry->d_name) + 1]; sprintf(tmpPathName, "%s/%s", pathName, dirEntry->d_name); countFile(tmpPathName); } closedir(dir); } void Session::create() { File infoFile(*dir, CDWserverInfo); infoFile.mkdirsParent(); FILE *outStream = fopen(infoFile.getPath().getBytes(), "w"); if (outStream == NULL) throw new Exception("internal error: Cannot create infofile."); fclose(outStream); } File *Session::getFirstTrack() { if (audioToWAV != NULL) closeAudioTrack(); currentTrackNo = 0; return getNextTrack(); } void Session::getInfo(const char *ID, int &numOfFiles, int &numOfTracks, int &size_KB) { File *dir = store->getSessionDir(ID); log.put(3, S.e + "dir = " + dir->getPath()); if (dir->exists()) readInfo(dir, numOfFiles, numOfTracks, size_KB); else numOfFiles = numOfTracks = size_KB = 0; delete dir; } int Session::getSessionSize(File &dir) { int sizeInKBytes = 0; File file(dir, CDWserverInfo); FILE *inStream = fopen(file.getPath().getBytes(), "r"); if (inStream == NULL) throw new Exception("internal error: can't read sessionInfo"); char lineStr[1024]; while (fgets(lineStr, sizeof lineStr, inStream) != NULL) { char str1[1024], str2[1024]; Config::parseLine(lineStr, str1, str2); if (strlen(str1) == 0 && strlen(str2) == 0) continue; else if (strcasecmp(str1, "sizeInKBytes") == 0) sscanf(str2, "%d", &sizeInKBytes); } fclose(inStream); return sizeInKBytes; } File *Session::getNextTrack() { File *nextTrack = NULL; DIR *dir = opendir(this->dir->getPath().getBytes()); if (dir == NULL) return NULL; struct dirent *dirEntry; int n; while ((dirEntry = readdir(dir)) != NULL) { String name = dirEntry->d_name; if (!name.endsWith(".inf")) if (sscanf(dirEntry->d_name, "track%d", &n) == 1) if (n == currentTrackNo + 1) { nextTrack = new File( this->dir, dirEntry->d_name); break; } } closedir(dir); if (nextTrack == NULL) return NULL; if (nextTrack->exists()) { if (currentTrack != NULL) delete currentTrack; currentTrack = nextTrack; currentTrackNo++; return currentTrack; } else { delete nextTrack; return NULL; } } int Session::isSessionDir(File &dir) { File file(dir, CDWserverInfo); return file.exists(); } void Session::linkFile(File &file, const char *linkName) { if (currentTrack == NULL || !currentTrack->isDirectory()) newTrack("dir"); checkContainsNoLink(linkName); File link = new File(currentTrack, linkName); link.mkdirsParent(); symlink(file.getPath().getBytes(), link.getPath().getBytes()); linksNum++; } void Session::linkTrack(File &track, const char *type) { int useableWAV = 0; if (strcasecmp(type, "wav") == 0) { try { Wave wave(track); useableWAV = wave.getAudioChannels() == 2 && wave.getAudioSampleBits() == 16 && wave.getAudioSampleRate() == 44100; } catch (Exception *e) { } } if (strcasecmp(type, "mp3") == 0 || strcasecmp(type, "ogg") == 0 || (strcasecmp(type, "wav") == 0 && !useableWAV)) { int in = open(track.getPath().getBytes(), O_RDONLY); if (in < 0) throw new Exception(S.e + "Cannot open track " + track.getPath() + " on the server"); InputStream inStream(in); try { newTrack(type); putTrack(inStream, track.length()); } catch (Exception *e) { close(in); throw e; } close(in); } else { newTrack(type, 1); symlink(track.getPath().getBytes(), currentTrack->getPath().getBytes()); } } void Session::makeDir(const char *fileName, int mode, int time) { if (currentTrack == NULL || !currentTrack->isDirectory()) newTrack("dir"); checkContainsNoLink(fileName); File dir = new File(currentTrack, fileName); dir.mkdirs(); dir.setLastModified(time); } void Session::newTrack(const char *trackType, int link) { if (audioToWAV != NULL) closeAudioTrack(); const char *extension = ""; if (strcasecmp(trackType, "image") == 0 || strcasecmp(trackType, "iso") == 0) extension = ".iso"; else if (strcasecmp(trackType, "inf") == 0) { if (currentTrack != NULL) { String name = currentTrack->getName(); if (name.length() > 3) { name = name.substring(0, name.length() - 4); name = name + ".inf"; *currentTrack = new File(dir, name.getBytes()); return; } } } else if (strcasecmp(trackType, "wav") == 0) { extension = ".wav"; if (!link) audioToWAV = new WAVToWAV(); } else if (strcasecmp(trackType, "mp3") == 0) { extension = ".wav"; if (!link) audioToWAV = new MP3ToWAV(); } else if (strcasecmp(trackType, "ogg") == 0) { extension = ".wav"; if (!link) audioToWAV = new OggToWAV(); } tracksNum++; char nextTrackName[20]; sprintf(nextTrackName, "track%03d%s", tracksNum, extension); if (currentTrack != NULL) { delete currentTrack; currentTrack = NULL; } currentTrack = new File(dir, nextTrackName); currentTrackNo = tracksNum; } void Session::putDummy(String &head, const char *fileName, int length, int mode, int time) { putFileStart(fileName); const int bufSize = 64 * 1024; char *buf = new char[bufSize]; try { copy(head.getBytes(), fileOutputStream, head.length()); length -= head.length(); // char buf[16 * 1024]; for (int i = 0; i < bufSize; i++) buf[i] = 0; while (length > 0) { int n = bufSize; if (n > length) n = length; copy(buf, fileOutputStream, n); length -= n; } delete[] buf; } catch (Exception *e) { delete[] buf; putFileEnd(mode, time); throw e; } putFileEnd(mode, time); } void Session::putFile(InputStream &inStream, const char *fileName, int length, int mode, int time) { try { putFileStart(fileName); } catch (Exception *e) { inStream.skip(length); throw e; } try { putFileContinue(inStream, length); } catch (Exception *e) { putFileEnd(mode, time); throw e; } putFileEnd(mode, time); } void Session::putFileContinue(InputStream &inStream, int length) { if (fileOutputStream == NULL) { inStream.skip(length); putFileContinue((const char *)NULL, 0); } copy(inStream, fileOutputStream, length); } void Session::putFileContinue(const char *buf, int length) { if (fileOutputStream == NULL) { log.put("Session::putFileContinue: no open file"); throw new Exception("no open file"); } copy(buf, fileOutputStream, length); } void Session::putFileEnd(int mode, int time) { if (fileOutputStream == NULL) return; fclose(fileOutputStream); fileOutputStream = NULL; if (mode >= 0) { chmod(currentFile.getPath().getBytes(), mode); currentFile.setLastModified(time); } } void Session::putFileStart(const char *fileName) { if (fileOutputStream != NULL) putFileEnd(); if (currentTrack == NULL || !currentTrack->isDirectory()) newTrack("dir"); checkContainsNoLink(fileName); currentFile = new File(currentTrack, fileName); currentFile.mkdirsParent(); currentFile.remove(); fileOutputStream = fopen(currentFile.getPath().getBytes(), "w"); if (fileOutputStream == NULL) { log.put("Session::putFileStart: fopen"); throw new Exception("cannot open file"); } filesNum++; } void Session::putTrack(InputStream &inStream, int length) { FILE *outStream = fopen(currentTrack->getPath().getBytes(), "a"); if (outStream == NULL) { inStream.skip(length); throw new Exception("cannot store track"); } try { if (audioToWAV != NULL) { audioToWAV->setInStream(&inStream); audioToWAV->put(length); while (1) { const char *buf; int n = audioToWAV->get(&buf); if (n == 0) break; try { copy(buf, outStream, n); } catch (Exception *e) { while (audioToWAV->get(&buf) > 0); throw e; } } } else copy(inStream, outStream, length); } catch (Exception *e) { fclose(outStream); throw e; } fclose(outStream); } void Session::readInfo() { int size_KB = sizeToKBytes(sizeInBytes); readInfo(dir, filesNum, tracksNum, size_KB); sizeInBytes = ((int64) 1024) * size_KB; } void Session::readInfo(File *dir, int &numOfFiles, int &numOfTracks, int &size_KB) { File file(dir, CDWserverInfo); FILE *inStream = fopen(file.getPath().getBytes(), "r"); if (inStream == NULL) throw new Exception("internal error: Cannot read sessionInfo."); char lineStr[1024]; while (fgets(lineStr, sizeof lineStr, inStream) != NULL) { char str1[1024], str2[1024]; Config::parseLine(lineStr, str1, str2); if (strlen(str1) == 0 && strlen(str2) == 0) continue; else if (strcasecmp(str1, "files") == 0) sscanf(str2, "%d", &numOfFiles); else if (strcasecmp(str1, "sizeInKBytes") == 0) sscanf(str2, "%d", &size_KB); else if (strcasecmp(str1, "tracks") == 0) sscanf(str2, "%d", &numOfTracks); } fclose(inStream); log.put("read previous session: " "files=%d, tracks=%d, size_KB=%d", numOfFiles, numOfTracks, size_KB); } void Session::remove(const char *fileName) { if (currentTrack == NULL || !currentTrack->isDirectory()) newTrack("dir"); checkContainsNoLink(fileName); File file = new File(currentTrack, fileName); file.mkdirsParent(); file.remove(); } void Session::writeInfo() { File file = new File(dir, CDWserverInfo); FILE *outStream = fopen(file.getPath().getBytes(), "w"); if (outStream != NULL) { fprintf(outStream, "sizeInKBytes=%d\n", sizeToKBytes(sizeInBytes)); fprintf(outStream, "files=%d\n", filesNum); fprintf(outStream, "tracks=%d\n", tracksNum); fclose(outStream); } }