/* Museek - Museek's 'core' library * * Copyright (C) 2003-2004 Hyriand * * 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 #include #include #include #define MULOG_DOMAIN "Museek.SH" #include #include #include #include #include #include #include using std::string; using std::wstring; using std::map; using std::vector; using std::pair; #include ShareBase::ShareBase(Museek* museek) : mMuseek(museek), mNumFolders(0), mNumFiles(0) { CT("ShareBase"); } void ShareBase::load(const string& db, const string& type) { CT("load %s", db.c_str()); mShares.load(db); update(type); } void ShareBase::update(const string& type) { CT("update"); recode(); update_flat(); update_compressed(); update_word_maps(); mNumFolders = mRecoded.folders.size(); mNumFiles = mFlat.size(); DEBUG("updated shares db, %i folders, %i files", mNumFolders, mNumFiles); if(mMuseek->logged_in()) if (type != "buddy") mMuseek->server_shared_folders_files(mNumFolders, mNumFiles); else if (type == "buddy" && mMuseek->mBuddySharesHave && mMuseek->mBuddiesOnly) mMuseek->server_shared_folders_files(mNumFolders, mNumFiles); } void ShareBase::recode() { CT("recode"); mRecoded.folders.clear(); std::map::iterator it = mShares.folders.begin(); for(; it != mShares.folders.end(); ++it) { std::string _redir = mMuseek->recoder()->fs_to_network((*it).first); if(_redir.empty()) { DEBUG("WARNING: Couldn't transcode '%s' to network encoding", (*it).first.c_str()); continue; } Folder::iterator fit = (*it).second->files.begin(); Folder _refolder; for(; fit != (*it).second->files.end(); ++fit) { std::string _refn = mMuseek->recoder()->fs_to_network((*fit).first); if(_refn.empty()) { DEBUG("WARNING: Couldn't transcode '%s' to network encoding", (*fit).first.c_str()); continue; } _refolder[_refn] = (*fit).second; } DirEntry* de = new DirEntry(_redir); de->files = _refolder; mRecoded.folders[_redir] = de; } } void ShareBase::update_flat() { CT("update_flat"); mFlat.clear(); mRecoded.flatten(mFlat); } void ShareBase::update_compressed() { CT("update_compressed"); mCompressed.clear(); std::queue data; mRecoded.network_pack(data); uLong outbuf_len = (int)(data.size() * 1.1 + 12), i = 0; char *inbuf = new char[data.size()], *outbuf = new char[outbuf_len]; while(! data.empty()) { inbuf[i++] = data.front(); data.pop(); } if (compress((Bytef *)outbuf, &outbuf_len, (Bytef *)inbuf, i) == Z_OK) { for(uint i = 0; i < outbuf_len; i++) mCompressed.push_back(outbuf[i]); } else DEBUG("compression error"); delete [] outbuf; delete [] inbuf; } bool ShareBase::is_shared(const string& path) const { return mFlat.find(path) != mFlat.end(); } /* this is a bit hairy... */ inline wchar_t mutate(wchar_t c, bool special = false) { if(c >= 'A' && c <= 'Z') return c | 32; switch(c) { case '/': case ' ': case ';': case ':': case '\'': case '\\': case ']': case '[': case '{': case '}': case '<': case '>': case ',': case '.': case '!': case '@': case '#': case '$': case '%': case '^': case '&': case '(': case ')': case '_': case '+': case '=': case '~': case '`': case '"': return ' '; case '-': case '*': if(! special) return ' '; default: return c; } } void ShareBase::update_word_maps() { CT("update_wordmaps"); mCharMap.clear(); // Generate the search map (used for searching) Folder::iterator fit = mFlat.begin(); for(; fit != mFlat.end(); ++fit) { wstring entry = mMuseek->recoder()->decode_network((*fit).first), word; wstring::const_iterator sit = entry.begin(); for(; sit != entry.end(); ++sit) { wchar_t c = mutate(*sit); if(c == ' ') { if(! word.empty()) { mCharMap[word[0]][word][(*fit).first] = (*fit).second; } word = wstring(); } else word += c; } if(! word.empty()) { mCharMap[word[0]][word][(*fit).first] = (*fit).second; } } } inline map ShareBase::fetch(const std::wstring& word) const { map > >::const_iterator it1 = mCharMap.find(word[0]); if(it1 != mCharMap.end()) { map >::const_iterator it2 = (*it1).second.find(word); if(it2 != (*it1).second.end()) return (*it2).second; } return map(); } /* this is the best I can do I think... */ void ShareBase::search(const wstring& _query, Folder& result) { CT("search %s", _query.c_str()); wstring query = _query; size_t results = 0; wstring word; if(query.empty()) return; /* add a space to make sure we also get the last word */ query += (wchar_t)' '; vector* > q_in; StringList q_out; vector q_part; bool quoted = false, was_quoted = false; /* breaks up the query into in-groups, out-files and terms */ wstring::iterator sit = query.begin(); for(; sit != query.end(); ++sit) { if(*sit == '"') { quoted = ! quoted; was_quoted = true; continue; } wchar_t c = mutate(*sit, quoted ? false : word.empty()); if(! quoted && c == ' ') { wchar_t c2 = word[0]; if(was_quoted || c2 == '*') q_part.push_back(word); else if(c2 == '-') { q_out.push_back(mMuseek->recoder()->encode_network(wstring(word.data() + 1, word.size() - 1))); } else { /* find files that match this word */ map* t = &mCharMap[word[0]][word]; if(! t->empty()) q_in.push_back(t); else return; } was_quoted = false; word = wstring(); continue; } word += c; } if(q_in.empty()) return; map* base = q_in[0]; vector* >::const_iterator first = q_in.begin()++; map::const_iterator it = base->begin(); for(; it != base->end(); ++it) { if(result.find((*it).first) != result.end()) continue; if(! q_out.empty()) { string lowr = tolower((*it).first); StringList::const_iterator oit = q_out.begin(); for(; oit != q_out.end(); ++oit) if(lowr.find(*oit) != string::npos) break; if(oit != q_out.end()) continue; } vector* >::const_iterator ref = first; for(; ref != q_in.end(); ++ref) if((*ref)->find((*it).first) == (*ref)->end()) break; if(ref == q_in.end()) { result[(*it).first] = (*it).second; ++results; } if(results >= 500) return; } return; } Shares ShareBase::folder_contents(const std::string& _f) { CT("folder_contents %s", _f.c_str()); Shares r_map; if(_f.empty()) return r_map; std::string q = str_replace(_f, '\\', '/'); if(q[q.size()-1] == '/') q = q.substr(0, q.size()-1); if(q.empty()) return r_map; std::map::iterator it = mRecoded.folders.begin(); for(; it != mRecoded.folders.end(); ++it) { if((*it).first == q || (*it).first.substr(0, q.size()+1) == q + "/" || (*it).first.substr(0, q.size()) == q ) { r_map[(*it).first ] = (*it).second->files; } } return r_map; }