ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/CCSFinger/CCSFinger.cpp
Revision: 617
Committed: 2005-12-13T01:35:20-08:00 (19 years, 6 months ago) by douglas
File size: 9734 byte(s)
Log Message:
Moo!

File Contents

# Content
1 // Douglas Thrift
2 //
3 // CCS Computer Science
4 //
5 // Windows Finger Daemon
6
7 #include <windows.h>
8 #include <tchar.h>
9 #include <signal.h>
10 #include <wtsapi32.h>
11 #include <lm.h>
12 #include <shlobj.h>
13
14 #include <algorithm>
15 #include <iostream>
16 #include <iomanip>
17 #include <string>
18 #include <sstream>
19 #include <vector>
20 #include <map>
21 #include <cwctype>
22
23 std::ostream &operator<<(std::ostream &out, WTS_CONNECTSTATE_CLASS status)
24 {
25 switch (status)
26 {
27 case WTSActive:
28 return out << "Active";
29
30 case WTSConnected:
31 return out << "Connected";
32
33 case WTSConnectQuery:
34 return out << "Connecting";
35
36 case WTSShadow:
37 return out << "Shadowing";
38
39 case WTSDisconnected:
40 return out << "Disconnected";
41
42 case WTSIdle:
43 return out << "Idle";
44
45 case WTSListen:
46 return out << "Listening";
47
48 case WTSReset:
49 return out << "Reseting";
50
51 case WTSDown:
52 return out << "Down";
53
54 case WTSInit:
55 return out << "Initializing";
56
57 default:
58 return out << "Unknown";
59 }
60 }
61
62 inline std::string Utf8(const std::wstring &wstring)
63 {
64 std::string string(::WideCharToMultiByte(CP_UTF8, 0, wstring.data(), int(wstring.size()), NULL, 0, NULL, NULL), '\0');
65
66 ::WideCharToMultiByte(CP_UTF8, 0, wstring.data(), int(wstring.size()), const_cast<LPSTR>(string.data()), int(string.size()), NULL, NULL);
67
68 return string;
69 }
70
71 class Finger
72 {
73 class Login
74 {
75 std::wstring login;
76 mutable USER_INFO_11 *info;
77
78 inline void GetInfo() const
79 {
80 if (!info)
81 ::NetUserGetInfo(NULL, login.c_str(), 11, reinterpret_cast<LPBYTE *>(&info));
82 }
83 public:
84 std::string session;
85 DWORD id;
86 WTS_CONNECTSTATE_CLASS status;
87
88 Login(const std::wstring &login, DWORD id, const std::wstring &session, WTS_CONNECTSTATE_CLASS status) : login(login), info(NULL), id(id), session(Utf8(session)), status(status)
89 {
90 }
91
92 ~Login()
93 {
94 if (!info)
95 ::NetApiBufferFree(info);
96 }
97
98 std::string GetShortName() const
99 {
100 GetInfo();
101
102 return Utf8(std::wstring(info->usri11_full_name).substr(0, 20));
103 }
104
105 std::string GetName() const
106 {
107 GetInfo();
108
109 return Utf8(info->usri11_full_name);
110 }
111
112 std::string GetDirectory() const
113 {
114 HANDLE token;
115
116 if (::WTSQueryUserToken(id, &token))
117 {
118 TCHAR directory[MAX_PATH];
119
120 ::SHGetFolderPath(NULL, CSIDL_PROFILE, token, SHGFP_TYPE_CURRENT, directory);
121 ::CloseHandle(token);
122
123 return Utf8(directory);
124 }
125 else
126 {
127 TCHAR directory_[MAX_PATH];
128
129 ::SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, directory_);
130
131 std::wstring directory(directory_);
132
133 directory.replace(directory.rfind('\\') + 1, std::wstring::npos, login);
134
135 return Utf8(directory);
136 }
137 }
138 };
139
140 std::ostringstream stream;
141 std::multimap<std::string, Login> logins;
142
143 void Full()
144 {
145 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); login = logins.upper_bound(login->first))
146 {
147 stream << "Login: " << login->first << std::string(33 - login->first.size(), ' ') << "Name: " << login->second.GetName() << "\r\nDirectory: " << login->second.GetDirectory() << "\r\n";
148 }
149 }
150
151 public:
152 Finger(bool full)
153 {
154 PWTS_SESSION_INFO sessions;
155 DWORD count;
156
157 ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count);
158
159 for (PWTS_SESSION_INFO session(sessions); session != sessions + count; ++session)
160 {
161 LPTSTR name;
162 DWORD size;
163
164 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSUserName, &name, &size);
165
166 std::wstring name_(name);
167
168 ::WTSFreeMemory(name);
169
170 if (!name_.empty())
171 {
172 std::transform(name_.begin(), name_.end(), name_.begin(), std::towlower);
173
174 LPTSTR session_;
175
176 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSWinStationName, &session_, &size);
177
178 Login login(name_, session->SessionId, session_, session->State);
179
180 ::WTSFreeMemory(session_);
181
182 logins.insert(std::make_pair(Utf8(name_), login));
183 }
184 }
185
186 ::WTSFreeMemory(sessions);
187
188 if (!full)
189 {
190 stream << "Login" << std::string(11, ' ') << "Name" << std::string(17, ' ') << "Id Session Status\r\n";
191
192 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); ++login)
193 stream << login->first << std::string(16 - login->first.size(), ' ') << login->second.GetShortName() << std::string(21 - login->second.GetShortName().size(), ' ') << std::setw(5) << std::left << login->second.id << " " << login->second.session << std::string(14 - login->second.session.size(), ' ') << login->second.status << "\r\n";
194 }
195 else
196 Full();
197 }
198
199 Finger(const std::string &name)
200 {
201 //NetQueryDisplayInformation
202 //NetUserGetInfo
203 }
204
205 inline operator std::string()
206 {
207 return stream.str();
208 }
209 };
210
211 LPTSTR name;
212 SERVICE_STATUS status;
213 SERVICE_STATUS_HANDLE handle;
214 HANDLE stop;
215 WSADATA data;
216 std::vector<HANDLE> threads;
217
218 void FingerDaemon();
219 DWORD WINAPI FingerListen(LPVOID server_);
220 DWORD WINAPI FingerDo(LPVOID client_);
221 void FingerStop(int);
222 void WINAPI FingerMain(DWORD argc, LPTSTR *argv);
223 void WINAPI FingerControl(DWORD control);
224
225 int _tmain(int argc, TCHAR *argv[])
226 {
227 SERVICE_TABLE_ENTRY entry[] = { { TEXT("CCSFinger"), LPSERVICE_MAIN_FUNCTION(FingerMain) }, { NULL, NULL } };
228
229 if (!::StartServiceCtrlDispatcher(entry))
230 {
231 DWORD error(::GetLastError());
232
233 switch (error)
234 {
235 case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
236 goto go;
237
238 case ERROR_INVALID_DATA:
239 std::cerr << "ERROR_INVALID_DATA" << std::endl;
240
241 break;
242
243 case ERROR_SERVICE_ALREADY_RUNNING:
244 std::cerr << "ERROR_SERVICE_ALREADY_RUNNING" << std::endl;
245
246 break;
247
248 default:
249 std::cerr << error << std::endl;
250 }
251
252 return 1;
253 }
254
255 return 0;
256
257 go:
258 for (int index(1); index != argc; ++index)
259 {
260 std::wstring arg(argv[index]);
261
262 if (arg == TEXT("create"))
263 {
264 TCHAR file[MAX_PATH];
265
266 ::GetModuleFileName(NULL, file, MAX_PATH);
267
268 SC_HANDLE manager(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE)), service(::CreateService(manager, TEXT("CCSFinger"), TEXT("CCS Finger Daemon"), 0, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, file, NULL, NULL, TEXT("TermService\0"), NULL, TEXT("")));
269
270 if (!service)
271 std::cerr << ::GetLastError() << std::endl;
272 else
273 ::CloseServiceHandle(service);
274
275 ::CloseServiceHandle(manager);
276
277 return 0;
278 }
279 else if (arg == TEXT("delete"))
280 {
281 SC_HANDLE manager(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE)), service(::OpenService(manager, TEXT("CCSFinger"), DELETE));
282
283 if (!::DeleteService(service))
284 std::cerr << ::GetLastError() << std::endl;
285
286 ::CloseServiceHandle(service);
287 ::CloseServiceHandle(manager);
288
289 return 0;
290 }
291 }
292
293 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
294
295 ::signal(SIGINT, FingerStop);
296
297 try
298 {
299 FingerDaemon();
300 }
301 catch (...) {}
302
303 return 0;
304 }
305
306 void FingerDaemon()
307 {
308 ::WSAStartup(MAKEWORD(2, 0), &data);
309
310 //
311
312 SOCKET server(::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
313 SOCKADDR_IN service;
314
315 service.sin_family = AF_INET;
316 service.sin_addr.s_addr = inet_addr("0.0.0.0");
317 service.sin_port = htons(79);
318
319 ::bind(server, (SOCKADDR *)(&service), sizeof (service));
320 ::listen(server, SOMAXCONN);
321
322 threads.push_back(::CreateThread(NULL, 0, FingerListen, &server, 0, NULL));
323
324 while(::WaitForSingleObject(stop, 1000) != WAIT_OBJECT_0);
325
326 //
327
328 ::closesocket(server);
329
330 ::WSACleanup();
331 }
332
333 DWORD WINAPI FingerListen(LPVOID server_)
334 {
335 SOCKET &server(*reinterpret_cast<SOCKET *>(server_));
336
337 while (true)
338 {
339 SOCKET client(accept(server, NULL, NULL));
340
341 threads.push_back(::CreateThread(NULL, 0, FingerDo, &client, 0, NULL));
342 }
343
344 return 0;
345 }
346
347 DWORD WINAPI FingerDo(LPVOID client_)
348 {
349 SOCKET &client(*reinterpret_cast<SOCKET *>(client_));
350 char buffer[1024];
351 std::istringstream stream(std::string(buffer, ::recv(client, buffer, sizeof buffer, 0)));
352 std::string line;
353
354 std::getline(stream, line);
355
356 stream.str(line);
357
358 std::getline(stream, line, '\r');
359
360 stream.str(line);
361
362 std::string name;
363 bool full(false);
364
365 while (stream >> line)
366 if (line == "/W")
367 full = true;
368 else
369 name = line;
370
371 std::string finger(name.empty() ? Finger(full) : Finger(name));
372
373 ::send(client, finger.data(), int(finger.size()), 0);
374 ::closesocket(client);
375
376 return 0;
377 }
378
379 void FingerStop(int)
380 {
381 ::SetEvent(stop);
382 }
383
384 void WINAPI FingerMain(DWORD argc, LPTSTR *argv)
385 {
386 name = argv[0];
387 handle = RegisterServiceCtrlHandler(name, LPHANDLER_FUNCTION(FingerControl));
388 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
389
390 //
391
392 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
393 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
394 status.dwServiceSpecificExitCode = 0;
395 status.dwCurrentState = SERVICE_RUNNING;
396 status.dwWin32ExitCode = NO_ERROR;
397 status.dwCheckPoint = 0;
398 status.dwWaitHint = 0;
399
400 ::SetServiceStatus(handle, &status);
401
402 try
403 {
404 FingerDaemon();
405 }
406 catch (...) {}
407
408 status.dwCurrentState = SERVICE_STOPPED;
409 status.dwWin32ExitCode = NO_ERROR;
410 status.dwCheckPoint = 0;
411 status.dwWaitHint = 0;
412
413 ::SetServiceStatus(handle, &status);
414 }
415
416 void WINAPI FingerControl(DWORD control)
417 {
418 switch (control)
419 {
420 case SERVICE_CONTROL_STOP:
421 case SERVICE_CONTROL_SHUTDOWN:
422 status.dwCurrentState = SERVICE_STOP_PENDING;
423
424 ::SetEvent(stop);
425
426 break;
427
428 case SERVICE_CONTROL_INTERROGATE:
429 break;
430 }
431
432 ::SetServiceStatus(handle, &status);
433 }