ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/CCSFinger/CCSFinger.cpp
Revision: 619
Committed: 2005-12-13T02:27:26-08:00 (19 years, 6 months ago) by douglas
File size: 10664 byte(s)
Log Message:
Except that!

File Contents

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