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

# User Rev Content
1 douglas 594 // Douglas Thrift
2     //
3     // CCS Computer Science
4     //
5     // Windows Finger Daemon
6    
7     #include <windows.h>
8 douglas 618 #include <lm.h>
9     #include <signal.h>
10 douglas 594 #include <tchar.h>
11 douglas 618 #include <shlobj.h>
12 douglas 608 #include <wtsapi32.h>
13 douglas 594
14 douglas 616 #include <algorithm>
15 douglas 618 #include <cctype>
16     #include <cwctype>
17     #include <fstream>
18     #include <iomanip>
19 douglas 595 #include <iostream>
20 douglas 618 #include <map>
21     #include <sstream>
22 douglas 608 #include <string>
23 douglas 609 #include <vector>
24 douglas 595
25 douglas 615 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 douglas 610 inline std::string Utf8(const std::wstring &wstring)
65     {
66 douglas 613 std::string string(::WideCharToMultiByte(CP_UTF8, 0, wstring.data(), int(wstring.size()), NULL, 0, NULL, NULL), '\0');
67 douglas 610
68 douglas 613 ::WideCharToMultiByte(CP_UTF8, 0, wstring.data(), int(wstring.size()), const_cast<LPSTR>(string.data()), int(string.size()), NULL, NULL);
69 douglas 610
70     return string;
71     }
72    
73     class Finger
74     {
75 douglas 616 class Login
76 douglas 613 {
77 douglas 616 std::wstring login;
78     mutable USER_INFO_11 *info;
79    
80     inline void GetInfo() const
81     {
82 douglas 617 if (!info)
83     ::NetUserGetInfo(NULL, login.c_str(), 11, reinterpret_cast<LPBYTE *>(&info));
84 douglas 616 }
85     public:
86     std::string session;
87 douglas 615 DWORD id;
88     WTS_CONNECTSTATE_CLASS status;
89 douglas 616
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 douglas 613 {
92 douglas 616 }
93 douglas 614
94 douglas 616 ~Login()
95     {
96     if (!info)
97     ::NetApiBufferFree(info);
98 douglas 613 }
99 douglas 615
100 douglas 617 std::string GetShortName() const
101 douglas 613 {
102 douglas 617 GetInfo();
103 douglas 616
104     return Utf8(std::wstring(info->usri11_full_name).substr(0, 20));
105 douglas 613 }
106 douglas 617
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 douglas 613 };
141 douglas 616
142 douglas 610 std::ostringstream stream;
143 douglas 616 std::multimap<std::string, Login> logins;
144 douglas 610
145 douglas 616 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 douglas 619 if (login != logins.begin())
150     stream << "\r\n";
151    
152 douglas 618 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 douglas 616 }
186     }
187    
188 douglas 610 public:
189     Finger(bool full)
190     {
191     PWTS_SESSION_INFO sessions;
192     DWORD count;
193    
194 douglas 613 ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count);
195 douglas 610
196 douglas 613 for (PWTS_SESSION_INFO session(sessions); session != sessions + count; ++session)
197 douglas 610 {
198     LPTSTR name;
199     DWORD size;
200    
201 douglas 613 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSUserName, &name, &size);
202 douglas 610
203 douglas 616 std::wstring name_(name);
204    
205     ::WTSFreeMemory(name);
206    
207     if (!name_.empty())
208 douglas 613 {
209 douglas 616 std::transform(name_.begin(), name_.end(), name_.begin(), std::towlower);
210    
211 douglas 613 LPTSTR session_;
212 douglas 610
213 douglas 613 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSWinStationName, &session_, &size);
214 douglas 611
215 douglas 616 Login login(name_, session->SessionId, session_, session->State);
216 douglas 610
217 douglas 613 ::WTSFreeMemory(session_);
218    
219 douglas 616 logins.insert(std::make_pair(Utf8(name_), login));
220 douglas 613 }
221 douglas 610 }
222    
223 douglas 613 ::WTSFreeMemory(sessions);
224    
225     if (!full)
226     {
227 douglas 616 stream << "Login" << std::string(11, ' ') << "Name" << std::string(17, ' ') << "Id Session Status\r\n";
228 douglas 613
229 douglas 616 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); ++login)
230 douglas 617 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 douglas 613 }
232 douglas 616 else
233     Full();
234 douglas 610 }
235    
236     Finger(const std::string &name)
237     {
238 douglas 612 //NetQueryDisplayInformation
239     //NetUserGetInfo
240 douglas 610 }
241    
242     inline operator std::string()
243     {
244     return stream.str();
245     }
246     };
247    
248 douglas 595 LPTSTR name;
249 douglas 594 SERVICE_STATUS status;
250     SERVICE_STATUS_HANDLE handle;
251 douglas 595 HANDLE stop;
252 douglas 594 WSADATA data;
253 douglas 609 std::vector<HANDLE> threads;
254 douglas 594
255 douglas 596 void FingerDaemon();
256 douglas 609 DWORD WINAPI FingerListen(LPVOID server_);
257     DWORD WINAPI FingerDo(LPVOID client_);
258 douglas 596 void FingerStop(int);
259 douglas 595 void WINAPI FingerMain(DWORD argc, LPTSTR *argv);
260     void WINAPI FingerControl(DWORD control);
261 douglas 594
262     int _tmain(int argc, TCHAR *argv[])
263     {
264 douglas 617 SERVICE_TABLE_ENTRY entry[] = { { TEXT("CCSFinger"), LPSERVICE_MAIN_FUNCTION(FingerMain) }, { NULL, NULL } };
265 douglas 594
266 douglas 613 if (!::StartServiceCtrlDispatcher(entry))
267 douglas 594 {
268 douglas 613 DWORD error(::GetLastError());
269 douglas 594
270     switch (error)
271     {
272     case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
273 douglas 595 goto go;
274 douglas 594
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 douglas 595
294     go:
295 douglas 617 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 douglas 613 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
331 douglas 608
332 douglas 613 ::signal(SIGINT, FingerStop);
333 douglas 595
334 douglas 596 try
335     {
336     FingerDaemon();
337     }
338     catch (...) {}
339    
340 douglas 609 return 0;
341 douglas 594 }
342    
343 douglas 596 void FingerDaemon()
344 douglas 594 {
345 douglas 613 ::WSAStartup(MAKEWORD(2, 0), &data);
346 douglas 594
347     //
348    
349 douglas 613 SOCKET server(::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
350 douglas 594 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 douglas 613 ::bind(server, (SOCKADDR *)(&service), sizeof (service));
357     ::listen(server, SOMAXCONN);
358 douglas 594
359 douglas 613 threads.push_back(::CreateThread(NULL, 0, FingerListen, &server, 0, NULL));
360 douglas 608
361 douglas 613 while(::WaitForSingleObject(stop, 1000) != WAIT_OBJECT_0);
362 douglas 594
363     //
364    
365 douglas 613 ::closesocket(server);
366 douglas 608
367 douglas 613 ::WSACleanup();
368 douglas 596 }
369 douglas 594
370 douglas 609 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 douglas 613 threads.push_back(::CreateThread(NULL, 0, FingerDo, &client, 0, NULL));
379 douglas 609 }
380    
381     return 0;
382     }
383    
384     DWORD WINAPI FingerDo(LPVOID client_)
385     {
386     SOCKET &client(*reinterpret_cast<SOCKET *>(client_));
387 douglas 610 char buffer[1024];
388 douglas 613 std::istringstream stream(std::string(buffer, ::recv(client, buffer, sizeof buffer, 0)));
389 douglas 610 std::string line;
390 douglas 609
391 douglas 610 std::getline(stream, line);
392 douglas 609
393 douglas 610 stream.str(line);
394 douglas 609
395 douglas 610 std::getline(stream, line, '\r');
396 douglas 609
397 douglas 610 stream.str(line);
398 douglas 609
399 douglas 610 std::string name;
400     bool full(false);
401 douglas 609
402 douglas 610 while (stream >> line)
403     if (line == "/W")
404     full = true;
405     else
406     name = line;
407 douglas 609
408 douglas 610 std::string finger(name.empty() ? Finger(full) : Finger(name));
409 douglas 609
410 douglas 613 ::send(client, finger.data(), int(finger.size()), 0);
411     ::closesocket(client);
412 douglas 609
413     return 0;
414     }
415    
416 douglas 596 void FingerStop(int)
417     {
418 douglas 613 ::SetEvent(stop);
419 douglas 596 }
420    
421     void WINAPI FingerMain(DWORD argc, LPTSTR *argv)
422     {
423     name = argv[0];
424     handle = RegisterServiceCtrlHandler(name, LPHANDLER_FUNCTION(FingerControl));
425 douglas 613 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
426 douglas 596
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 douglas 613 ::SetServiceStatus(handle, &status);
438 douglas 596
439     try
440     {
441     FingerDaemon();
442     }
443     catch (...) {}
444    
445 douglas 595 status.dwCurrentState = SERVICE_STOPPED;
446 douglas 594 status.dwWin32ExitCode = NO_ERROR;
447     status.dwCheckPoint = 0;
448     status.dwWaitHint = 0;
449    
450 douglas 613 ::SetServiceStatus(handle, &status);
451 douglas 594 }
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 douglas 613 ::SetEvent(stop);
462 douglas 594
463     break;
464    
465     case SERVICE_CONTROL_INTERROGATE:
466     break;
467     }
468    
469 douglas 613 ::SetServiceStatus(handle, &status);
470 douglas 594 }