| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #include <AdblockPlus.h> | |
| 2 #include <iostream> | |
| 3 #include <ShlObj.h> | |
| 4 #include <sstream> | |
| 5 #include <vector> | |
| 6 #include <Windows.h> | |
| 7 #include <Sddl.h> | |
|
Wladimir Palant
2013/05/16 11:52:16
I think that the usual order of includes is from g
Felix Dahlke
2013/05/17 09:11:36
We're using alphabetical in most places, but it's
| |
| 8 | |
| 9 namespace | |
| 10 { | |
| 11 const std::wstring pipeName = L"\\\\.\\pipe\\adblockplusengine"; | |
| 12 const int bufferSize = 1024; | |
| 13 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine; | |
| 14 HANDLE filterEngineMutex; | |
| 15 | |
| 16 class AutoHandle | |
| 17 { | |
| 18 public: | |
| 19 AutoHandle() | |
| 20 { | |
| 21 } | |
| 22 | |
| 23 AutoHandle(HANDLE handle) : handle(handle) | |
| 24 { | |
| 25 } | |
| 26 | |
| 27 ~AutoHandle() | |
| 28 { | |
| 29 CloseHandle(handle); | |
| 30 } | |
| 31 | |
| 32 HANDLE get() | |
| 33 { | |
| 34 return handle; | |
| 35 } | |
| 36 | |
| 37 private: | |
| 38 HANDLE handle; | |
| 39 static int references; | |
|
Wladimir Palant
2013/05/16 11:52:16
Unused member?
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 40 | |
| 41 AutoHandle(const AutoHandle& autoHandle); | |
| 42 AutoHandle& operator=(const AutoHandle& autoHandle); | |
| 43 }; | |
| 44 | |
| 45 void Log(const std::string& message) | |
| 46 { | |
| 47 // TODO: Log to a log file | |
| 48 MessageBoxA(0, ("AdblockPlusEngine: " + message).c_str(), "", MB_OK); | |
| 49 } | |
| 50 | |
| 51 void LogLastError(const std::string& message) | |
| 52 { | |
| 53 std::stringstream stream; | |
| 54 stream << message << " (Error code: " << GetLastError() << ")"; | |
| 55 Log(stream.str()); | |
| 56 } | |
| 57 | |
| 58 void LogException(const std::exception& exception) | |
| 59 { | |
| 60 Log(std::string("An exception occurred: ") + exception.what()); | |
| 61 } | |
| 62 | |
| 63 std::string MarshalStrings(const std::vector<std::string>& strings) | |
| 64 { | |
| 65 // TODO: This is some pretty hacky marshalling, replace it with something mo re robust | |
|
Wladimir Palant
2013/05/16 11:52:16
That's an understatement :)
| |
| 66 std::string marshalledStrings; | |
| 67 for (std::vector<std::string>::const_iterator it = strings.begin(); it != st rings.end(); it++) | |
| 68 marshalledStrings += *it + ';'; | |
| 69 return marshalledStrings; | |
| 70 } | |
| 71 | |
| 72 std::vector<std::string> UnmarshalStrings(const std::string& message) | |
| 73 { | |
| 74 std::stringstream stream(message); | |
| 75 std::vector<std::string> strings; | |
| 76 std::string string; | |
| 77 while (std::getline(stream, string, ';')) | |
| 78 strings.push_back(string); | |
| 79 return strings; | |
| 80 } | |
| 81 | |
| 82 std::string ToString(std::wstring value) | |
| 83 { | |
| 84 int size = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, 0, 0, 0, 0); | |
|
Wladimir Palant
2013/05/16 11:52:16
WideCharToMultiByte() will return 0 on error, this
Felix Dahlke
2013/05/23 12:31:20
Done. Learnt something here: I thought std::string
| |
| 85 char* converted = new char[size]; | |
|
Wladimir Palant
2013/05/16 11:52:16
std::auto_ptr please, don't delete manually.
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 86 WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, converted, size, 0, 0); | |
| 87 std::string string(converted); | |
|
Wladimir Palant
2013/05/16 11:52:16
string(converted, size) please - you have an expli
Felix Dahlke
2013/05/17 09:11:36
Writing directly to the string buffer is still not
Felix Dahlke
2013/05/23 12:31:20
Decided to keep the function after all. ToUTF8Stri
Wladimir Palant
2013/05/23 14:25:01
No real objections but the function name should at
Felix Dahlke
2013/05/23 19:10:44
Just copied the function from libadblockplus, alth
| |
| 88 delete converted; | |
| 89 return string; | |
| 90 } | |
| 91 | |
| 92 std::string ReadMessage(HANDLE pipe) | |
| 93 { | |
| 94 std::stringstream stream; | |
| 95 std::auto_ptr<char> buffer(new char[bufferSize]); | |
| 96 bool hasError; | |
| 97 do | |
| 98 { | |
| 99 DWORD bytesRead; | |
| 100 hasError = !ReadFile(pipe, buffer.get(), bufferSize * sizeof(char), &bytes Read, 0); | |
| 101 if (hasError && GetLastError() != ERROR_MORE_DATA) | |
| 102 { | |
| 103 std::stringstream stream; | |
| 104 stream << "Error reading from pipe: " << GetLastError(); | |
| 105 throw std::runtime_error(stream.str()); | |
| 106 } | |
| 107 stream << std::string(buffer.get(), bytesRead); | |
| 108 } while (hasError); | |
|
Wladimir Palant
2013/05/16 11:52:16
The flow is rather hard to follow here, particular
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 109 return stream.str(); | |
| 110 } | |
| 111 | |
| 112 void WriteMessage(HANDLE pipe, const std::string& message) | |
| 113 { | |
| 114 DWORD bytesWritten; | |
| 115 if (!WriteFile(pipe, message.c_str(), message.length(), &bytesWritten, 0)) | |
| 116 throw std::runtime_error("Failed to write to pipe"); | |
|
Wladimir Palant
2013/05/16 11:52:16
What if bytesWritten < message.length()? According
Felix Dahlke
2013/05/17 09:11:36
Yes, it's blocking. And writing long strings works
| |
| 117 } | |
| 118 | |
| 119 std::string HandleRequest(const std::vector<std::string>& strings) | |
| 120 { | |
| 121 std::string procedureName = strings[0]; | |
| 122 if (procedureName == "Matches") | |
| 123 return filterEngine->Matches(strings[1], strings[2], strings[3]) ? "1" : " 0"; | |
| 124 if (procedureName == "GetElementHidingSelectors") | |
| 125 return MarshalStrings(filterEngine->GetElementHidingSelectors(strings[1])) ; | |
| 126 return ""; | |
| 127 } | |
| 128 | |
| 129 DWORD WINAPI ClientThread(LPVOID param) | |
| 130 { | |
| 131 HANDLE pipe = static_cast<HANDLE>(param); | |
| 132 | |
| 133 try | |
| 134 { | |
| 135 std::string message = ReadMessage(pipe); | |
| 136 std::vector<std::string> strings = UnmarshalStrings(message); | |
| 137 WaitForSingleObject(filterEngineMutex, INFINITE); | |
|
Wladimir Palant
2013/05/16 11:52:16
Doesn't this duplicate the functionality of mutexe
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 138 std::string response = HandleRequest(strings); | |
| 139 ReleaseMutex(filterEngineMutex); | |
| 140 WriteMessage(pipe, response); | |
| 141 } | |
| 142 catch (const std::exception& e) | |
| 143 { | |
| 144 LogException(e); | |
| 145 } | |
| 146 | |
| 147 FlushFileBuffers(pipe); | |
| 148 DisconnectNamedPipe(pipe); | |
| 149 CloseHandle(pipe); | |
|
Wladimir Palant
2013/05/16 11:52:16
Opening a new connection for each calls sounds lik
Felix Dahlke
2013/05/17 09:11:36
I previously read that there is a limit to the num
Felix Dahlke
2013/05/23 12:31:20
Began to work on this, but it's a bit tricky and I
| |
| 150 return 0; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 bool IsWindowsVistaOrLater() | |
|
Wladimir Palant
2013/05/16 11:52:16
Any reason why some functions are inside an empty
Felix Dahlke
2013/05/17 09:11:36
Forgot to move it there.
| |
| 155 { | |
| 156 OSVERSIONINFOEX osvi; | |
| 157 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); | |
| 158 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); | |
| 159 GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi)); | |
| 160 return osvi.dwMajorVersion >= 6; | |
| 161 } | |
| 162 | |
| 163 std::wstring GetAppDataPath() | |
| 164 { | |
| 165 wchar_t appDataPath[MAX_PATH]; | |
| 166 if (IsWindowsVistaOrLater()) | |
| 167 { | |
| 168 WCHAR* dataPath; | |
| 169 if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, 0, &dataPath))) | |
|
Wladimir Palant
2013/05/16 11:52:16
I don't think that statically compiling against SH
Felix Dahlke
2013/05/23 12:31:20
I'll leave this to Oleksandr, since he's working o
Oleksandr
2013/05/23 12:38:11
We are not statically compiling against SHGetKnown
| |
| 170 throw std::runtime_error("Unable to find app data directory"); | |
| 171 wcscpy_s(appDataPath, dataPath); | |
|
Wladimir Palant
2013/05/16 11:52:16
Why do we need two copy operations here? Convertin
Felix Dahlke
2013/05/17 09:11:36
I think that's because SHGetKnownFolderPath wants
Wladimir Palant
2013/05/17 10:35:02
Then maybe get rid of that assumption? :)
Declare
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 172 CoTaskMemFree(dataPath); | |
| 173 } | |
| 174 else | |
| 175 { | |
| 176 if (!SHGetSpecialFolderPath(0, appDataPath, CSIDL_LOCAL_APPDATA, true)) | |
| 177 throw std::runtime_error("Unable to find app data directory"); | |
| 178 } | |
| 179 return std::wstring(appDataPath) + L"\\AdblockPlus"; | |
| 180 } | |
| 181 | |
| 182 std::auto_ptr<AdblockPlus::FilterEngine> CreateFilterEngine() | |
| 183 { | |
| 184 // TODO: Pass appInfo in, which should be sent by the client | |
| 185 AdblockPlus::JsEnginePtr jsEngine = AdblockPlus::JsEngine::New(); | |
| 186 std::string dataPath = ToString(GetAppDataPath()); | |
| 187 dynamic_cast<AdblockPlus::DefaultFileSystem*>(jsEngine->GetFileSystem().get()) ->SetBasePath(dataPath); | |
| 188 std::auto_ptr<AdblockPlus::FilterEngine> filterEngine(new AdblockPlus::FilterE ngine(jsEngine)); | |
| 189 int timeout = 5000; | |
| 190 while (!filterEngine->IsInitialized()) | |
|
Wladimir Palant
2013/05/16 11:52:16
TODO comment here please - we don't really want to
Felix Dahlke
2013/05/17 09:11:36
I presume that's not necessary anymore, now that y
Wladimir Palant
2013/05/17 10:35:02
Not pushed yet but - yes, once that review is done
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 191 { | |
| 192 const int step = 10; | |
| 193 Sleep(step); | |
| 194 timeout -= step; | |
| 195 if (timeout <= 0) | |
| 196 throw std::runtime_error("Timeout while waiting for FilterEngine initializ ation"); | |
| 197 } | |
| 198 std::vector<AdblockPlus::SubscriptionPtr> subscriptions = filterEngine->FetchA vailableSubscriptions(); | |
| 199 // TODO: Select a subscription based on the language, not just the first one. | |
| 200 // This should ideally be done in libadblockplus. | |
| 201 AdblockPlus::SubscriptionPtr subscription = subscriptions[0]; | |
| 202 subscription->AddToList(); | |
| 203 return filterEngine; | |
| 204 } | |
| 205 | |
| 206 HANDLE CreatePipe(const std::wstring& pipeName) | |
| 207 { | |
| 208 SECURITY_ATTRIBUTES sa; | |
| 209 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); | |
| 210 sa.nLength = sizeof(SECURITY_ATTRIBUTES); | |
| 211 | |
| 212 // Low mandatory label. See http://msdn.microsoft.com/en-us/library/bb625958.a spx | |
| 213 LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"; | |
| 214 PSECURITY_DESCRIPTOR securitydescriptor; | |
| 215 ConvertStringSecurityDescriptorToSecurityDescriptor( | |
| 216 LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &securitydescriptor, 0); | |
|
Wladimir Palant
2013/05/16 11:52:16
You are leaking this security descriptor, it needs
Felix Dahlke
2013/05/23 12:31:20
Done.
| |
| 217 | |
| 218 sa.lpSecurityDescriptor = securitydescriptor; | |
| 219 sa.bInheritHandle = TRUE; | |
| 220 | |
| 221 return CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
| 222 PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &s a); | |
| 223 } | |
| 224 | |
| 225 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) | |
|
Wladimir Palant
2013/05/16 11:52:16
Second HINSTANCE parameter is hPrevInstance - you
Felix Dahlke
2013/05/23 12:31:20
Seems to be always 0 in Win32: http://blogs.msdn.c
Wladimir Palant
2013/05/23 14:25:01
Right, a bit of outdated knowledge on my end :-(
F
Felix Dahlke
2013/05/23 19:10:44
How about we just try to create the pipe initially
Wladimir Palant
2013/05/23 20:10:15
Yes, that's something I thought about as well. Thi
| |
| 226 { | |
| 227 filterEngine = CreateFilterEngine(); | |
| 228 filterEngineMutex = CreateMutex(0, false, 0); | |
| 229 | |
| 230 for (;;) | |
| 231 { | |
| 232 HANDLE pipe = CreatePipe(pipeName); | |
| 233 if (pipe == INVALID_HANDLE_VALUE) | |
| 234 { | |
| 235 LogLastError("CreateNamedPipe failed"); | |
| 236 return 1; | |
| 237 } | |
| 238 | |
| 239 if (!ConnectNamedPipe(pipe, 0)) | |
| 240 { | |
| 241 LogLastError("Client failed to connect"); | |
| 242 CloseHandle(pipe); | |
| 243 continue; | |
| 244 } | |
| 245 | |
| 246 // TODO: Count established connections, kill the engine when none are left | |
| 247 | |
| 248 AutoHandle thread(CreateThread(0, 0, ClientThread, static_cast<LPVOID>(pipe) , 0, 0)); | |
| 249 if (!thread.get()) | |
| 250 { | |
| 251 LogLastError("CreateThread failed"); | |
| 252 return 1; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 return 0; | |
| 257 } | |
|
Wladimir Palant
2013/05/16 11:52:16
IMHO, ReadMessage(), WriteMessage(), MarshalString
Felix Dahlke
2013/05/17 09:11:36
Same here, there's a TODO for that :)
| |
| OLD | NEW |