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 |