ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/CCSFinger/CCSFinger.cpp
Revision: 640
Committed: 2006-01-04T20:54:20-08:00 (19 years, 5 months ago) by douglas
File size: 11915 byte(s)
Log Message:
Hmm, not there yet...

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 inline std::wstring Utf8(const std::string &string)
74 {
75 std::wstring wstring(::MultiByteToWideChar(CP_UTF8, 0, string.data(), int(string.size()), NULL, 0), '\0');
76
77 ::MultiByteToWideChar(CP_UTF8, 0, string.data(), int(string.size()), const_cast<LPWSTR>(wstring.data()), int(wstring.size()));
78
79 return wstring;
80 }
81
82 class Finger
83 {
84 class Login
85 {
86 std::wstring login;
87 mutable USER_INFO_11 *info;
88
89 inline void GetInfo() const
90 {
91 if (!info)
92 ::NetUserGetInfo(NULL, login.c_str(), 11, reinterpret_cast<LPBYTE *>(&info));
93 }
94 public:
95 std::string session;
96 DWORD id;
97 WTS_CONNECTSTATE_CLASS status;
98
99 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)
100 {
101 }
102
103 ~Login()
104 {
105 if (!info)
106 ::NetApiBufferFree(info);
107 }
108
109 std::string GetShortName() const
110 {
111 GetInfo();
112
113 return Utf8(std::wstring(info->usri11_full_name).substr(0, 20));
114 }
115
116 std::string GetName() const
117 {
118 GetInfo();
119
120 return Utf8(info->usri11_full_name);
121 }
122
123 std::string GetDirectory() const
124 {
125 HANDLE token;
126
127 if (::WTSQueryUserToken(id, &token))
128 {
129 TCHAR directory[MAX_PATH];
130
131 ::SHGetFolderPath(NULL, CSIDL_PROFILE, token, SHGFP_TYPE_CURRENT, directory);
132 ::CloseHandle(token);
133
134 return Utf8(directory);
135 }
136 else
137 {
138 TCHAR directory_[MAX_PATH];
139
140 ::SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, directory_);
141
142 std::wstring directory(directory_);
143
144 directory.replace(directory.rfind('\\') + 1, std::wstring::npos, login);
145
146 return Utf8(directory);
147 }
148 }
149 };
150
151 std::ostringstream stream;
152 std::multimap<std::string, Login> logins;
153
154 void Full()
155 {
156 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); login = logins.upper_bound(login->first))
157 {
158 if (login != logins.begin())
159 stream << "\r\n";
160
161 std::string directory(login->second.GetDirectory());
162
163 stream << "Login: " << login->first << std::string(33 - login->first.size(), ' ') << "Name: " << login->second.GetName() << "\r\nDirectory: " << directory << "\r\n";
164
165 for (std::multimap<std::string, Login>::const_iterator login_(logins.lower_bound(login->first)); login_ != logins.upper_bound(login->first); ++login_)
166 stream << login_->second.status << ' ' << login_->second.session << ' ' << login_->second.id << "\r\n";
167
168 std::string files[] = { "\\.project", "\\.plan" };
169
170 for (std::string *file(files); file != files + sizeof (files) / sizeof (*file); ++file)
171 {
172 file->insert(0, directory);
173
174 std::ifstream in(file->c_str());
175
176 if (!in.is_open())
177 continue;
178
179 switch (file - files)
180 {
181 case 0:
182 stream << "Project:\r\n";
183
184 break;
185 case 1:
186 stream << "Plan:\r\n";
187 }
188
189 std::string buffer;
190
191 while (std::getline(in, buffer))
192 stream << buffer << std::endl;
193 }
194 }
195 }
196
197 public:
198 Finger(bool full)
199 {
200 PWTS_SESSION_INFO sessions;
201 DWORD count;
202
203 ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count);
204
205 for (PWTS_SESSION_INFO session(sessions); session != sessions + count; ++session)
206 {
207 LPTSTR name;
208 DWORD size;
209
210 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSUserName, &name, &size);
211
212 std::wstring name_(name);
213
214 ::WTSFreeMemory(name);
215
216 if (!name_.empty())
217 {
218 std::transform(name_.begin(), name_.end(), name_.begin(), std::towlower);
219
220 LPTSTR session_;
221
222 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSWinStationName, &session_, &size);
223
224 Login login(name_, session->SessionId, session_, session->State);
225
226 ::WTSFreeMemory(session_);
227
228 logins.insert(std::make_pair(Utf8(name_), login));
229 }
230 }
231
232 ::WTSFreeMemory(sessions);
233
234 if (!full)
235 {
236 stream << "Login" << std::string(11, ' ') << "Name" << std::string(17, ' ') << "Id Session Status\r\n";
237
238 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); ++login)
239 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";
240 }
241 else
242 Full();
243 }
244
245 Finger(const std::string &name_)
246 {
247 std::wstring name(Utf8(name_));
248
249 std::transform(name.begin(), name.end(), name.begin(), std::towlower);
250
251 DWORD size;
252 PNET_DISPLAY_USER users;
253
254 ::NetQueryDisplayInformation(NULL, 1, 0, 100, MAX_PREFERRED_LENGTH, &size, reinterpret_cast<PVOID *>(&users));
255
256 for (PNET_DISPLAY_USER user = users; user != users + size; ++user)
257 {
258 std::wstring name_(user->usri1_name);
259
260 std::transform(name_.begin(), name_.end(), name_.begin(), std::towlower);
261
262 if (name_.find(name) != std::wstring::npos)
263 ;//logins.insert(std::make_pair(Utf8(name_),
264 else
265 {
266 std::wstring full(user->usri1_full_name);
267
268 std::transform(full.begin(), full.end(), full.begin(), std::towlower);
269
270 if (full.find(name) != std::wstring::npos)
271 ;//logins.insert(std::make_pair(Utf8(name_),
272 }
273 }
274
275 ::NetApiBufferFree(users);
276
277 if (!logins.empty())
278 Full();
279 else
280 stream << "finger: " << name_ << ": no such user\r\n";
281 }
282
283 inline operator std::string()
284 {
285 return stream.str();
286 }
287 };
288
289 LPTSTR name;
290 SERVICE_STATUS status;
291 SERVICE_STATUS_HANDLE handle;
292 HANDLE stop;
293 WSADATA data;
294 std::vector<HANDLE> threads;
295
296 void FingerDaemon();
297 DWORD WINAPI FingerListen(LPVOID server_);
298 DWORD WINAPI FingerDo(LPVOID client_);
299 void FingerStop(int);
300 void WINAPI FingerMain(DWORD argc, LPTSTR *argv);
301 void WINAPI FingerControl(DWORD control);
302
303 int _tmain(int argc, TCHAR *argv[])
304 {
305 SERVICE_TABLE_ENTRY entry[] = { { TEXT("CCSFinger"), LPSERVICE_MAIN_FUNCTION(FingerMain) }, { NULL, NULL } };
306
307 if (!::StartServiceCtrlDispatcher(entry))
308 {
309 DWORD error(::GetLastError());
310
311 switch (error)
312 {
313 case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
314 goto go;
315
316 case ERROR_INVALID_DATA:
317 std::cerr << "ERROR_INVALID_DATA" << std::endl;
318
319 break;
320
321 case ERROR_SERVICE_ALREADY_RUNNING:
322 std::cerr << "ERROR_SERVICE_ALREADY_RUNNING" << std::endl;
323
324 break;
325
326 default:
327 std::cerr << error << std::endl;
328 }
329
330 return 1;
331 }
332
333 return 0;
334
335 go:
336 for (int index(1); index != argc; ++index)
337 {
338 std::wstring arg(argv[index]);
339
340 if (arg == TEXT("create"))
341 {
342 TCHAR file[MAX_PATH];
343
344 ::GetModuleFileName(NULL, file, MAX_PATH);
345
346 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("")));
347
348 if (!service)
349 std::cerr << ::GetLastError() << std::endl;
350 else
351 ::CloseServiceHandle(service);
352
353 ::CloseServiceHandle(manager);
354
355 return 0;
356 }
357 else if (arg == TEXT("delete"))
358 {
359 SC_HANDLE manager(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE)), service(::OpenService(manager, TEXT("CCSFinger"), DELETE));
360
361 if (!::DeleteService(service))
362 std::cerr << ::GetLastError() << std::endl;
363
364 ::CloseServiceHandle(service);
365 ::CloseServiceHandle(manager);
366
367 return 0;
368 }
369 }
370
371 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
372
373 ::signal(SIGINT, FingerStop);
374
375 try
376 {
377 FingerDaemon();
378 }
379 catch (...) {}
380
381 return 0;
382 }
383
384 void FingerDaemon()
385 {
386 ::WSAStartup(MAKEWORD(2, 0), &data);
387
388 //
389
390 SOCKET server(::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
391 SOCKADDR_IN service;
392
393 service.sin_family = AF_INET;
394 service.sin_addr.s_addr = inet_addr("0.0.0.0");
395 service.sin_port = htons(79);
396
397 ::bind(server, (SOCKADDR *)(&service), sizeof (service));
398 ::listen(server, SOMAXCONN);
399
400 threads.push_back(::CreateThread(NULL, 0, FingerListen, &server, 0, NULL));
401
402 while(::WaitForSingleObject(stop, 1000) != WAIT_OBJECT_0);
403
404 //
405
406 ::closesocket(server);
407
408 ::WSACleanup();
409 }
410
411 DWORD WINAPI FingerListen(LPVOID server_)
412 {
413 SOCKET &server(*reinterpret_cast<SOCKET *>(server_));
414
415 while (true)
416 {
417 SOCKET client(accept(server, NULL, NULL));
418
419 threads.push_back(::CreateThread(NULL, 0, FingerDo, &client, 0, NULL));
420 }
421
422 return 0;
423 }
424
425 DWORD WINAPI FingerDo(LPVOID client_)
426 {
427 SOCKET &client(*reinterpret_cast<SOCKET *>(client_));
428 char buffer[1024];
429 std::istringstream stream(std::string(buffer, ::recv(client, buffer, sizeof buffer, 0)));
430 std::string line;
431
432 std::getline(stream, line);
433
434 stream.str(line);
435
436 std::getline(stream, line, '\r');
437
438 stream.str(line);
439
440 std::string name;
441 bool full(false);
442
443 while (stream >> line)
444 if (line == "/W")
445 full = true;
446 else
447 name = line;
448
449 std::string finger(name.empty() ? Finger(full) : Finger(name));
450
451 ::send(client, finger.data(), int(finger.size()), 0);
452 ::closesocket(client);
453
454 return 0;
455 }
456
457 void FingerStop(int)
458 {
459 ::SetEvent(stop);
460 }
461
462 void WINAPI FingerMain(DWORD argc, LPTSTR *argv)
463 {
464 name = argv[0];
465 handle = RegisterServiceCtrlHandler(name, LPHANDLER_FUNCTION(FingerControl));
466 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
467
468 //
469
470 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
471 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
472 status.dwServiceSpecificExitCode = 0;
473 status.dwCurrentState = SERVICE_RUNNING;
474 status.dwWin32ExitCode = NO_ERROR;
475 status.dwCheckPoint = 0;
476 status.dwWaitHint = 0;
477
478 ::SetServiceStatus(handle, &status);
479
480 try
481 {
482 FingerDaemon();
483 }
484 catch (...) {}
485
486 status.dwCurrentState = SERVICE_STOPPED;
487 status.dwWin32ExitCode = NO_ERROR;
488 status.dwCheckPoint = 0;
489 status.dwWaitHint = 0;
490
491 ::SetServiceStatus(handle, &status);
492 }
493
494 void WINAPI FingerControl(DWORD control)
495 {
496 switch (control)
497 {
498 case SERVICE_CONTROL_STOP:
499 case SERVICE_CONTROL_SHUTDOWN:
500 status.dwCurrentState = SERVICE_STOP_PENDING;
501
502 ::SetEvent(stop);
503
504 break;
505
506 case SERVICE_CONTROL_INTERROGATE:
507 break;
508 }
509
510 ::SetServiceStatus(handle, &status);
511 }