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

# 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 douglas 640 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 douglas 610 class Finger
83     {
84 douglas 616 class Login
85 douglas 613 {
86 douglas 616 std::wstring login;
87     mutable USER_INFO_11 *info;
88    
89     inline void GetInfo() const
90     {
91 douglas 617 if (!info)
92     ::NetUserGetInfo(NULL, login.c_str(), 11, reinterpret_cast<LPBYTE *>(&info));
93 douglas 616 }
94     public:
95     std::string session;
96 douglas 615 DWORD id;
97     WTS_CONNECTSTATE_CLASS status;
98 douglas 616
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 douglas 613 {
101 douglas 616 }
102 douglas 614
103 douglas 616 ~Login()
104     {
105     if (!info)
106     ::NetApiBufferFree(info);
107 douglas 613 }
108 douglas 615
109 douglas 617 std::string GetShortName() const
110 douglas 613 {
111 douglas 617 GetInfo();
112 douglas 616
113     return Utf8(std::wstring(info->usri11_full_name).substr(0, 20));
114 douglas 613 }
115 douglas 617
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 douglas 613 };
150 douglas 616
151 douglas 610 std::ostringstream stream;
152 douglas 616 std::multimap<std::string, Login> logins;
153 douglas 610
154 douglas 616 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 douglas 619 if (login != logins.begin())
159     stream << "\r\n";
160    
161 douglas 618 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 douglas 616 }
195     }
196    
197 douglas 610 public:
198     Finger(bool full)
199     {
200     PWTS_SESSION_INFO sessions;
201     DWORD count;
202    
203 douglas 613 ::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count);
204 douglas 610
205 douglas 613 for (PWTS_SESSION_INFO session(sessions); session != sessions + count; ++session)
206 douglas 610 {
207     LPTSTR name;
208     DWORD size;
209    
210 douglas 613 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSUserName, &name, &size);
211 douglas 610
212 douglas 616 std::wstring name_(name);
213    
214     ::WTSFreeMemory(name);
215    
216     if (!name_.empty())
217 douglas 613 {
218 douglas 616 std::transform(name_.begin(), name_.end(), name_.begin(), std::towlower);
219    
220 douglas 613 LPTSTR session_;
221 douglas 610
222 douglas 613 ::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSWinStationName, &session_, &size);
223 douglas 611
224 douglas 616 Login login(name_, session->SessionId, session_, session->State);
225 douglas 610
226 douglas 613 ::WTSFreeMemory(session_);
227    
228 douglas 616 logins.insert(std::make_pair(Utf8(name_), login));
229 douglas 613 }
230 douglas 610 }
231    
232 douglas 613 ::WTSFreeMemory(sessions);
233    
234     if (!full)
235     {
236 douglas 616 stream << "Login" << std::string(11, ' ') << "Name" << std::string(17, ' ') << "Id Session Status\r\n";
237 douglas 613
238 douglas 616 for (std::multimap<std::string, Login>::const_iterator login(logins.begin()); login != logins.end(); ++login)
239 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";
240 douglas 613 }
241 douglas 616 else
242     Full();
243 douglas 610 }
244    
245 douglas 640 Finger(const std::string &name_)
246 douglas 610 {
247 douglas 640 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 douglas 610 }
282    
283     inline operator std::string()
284     {
285     return stream.str();
286     }
287     };
288    
289 douglas 595 LPTSTR name;
290 douglas 594 SERVICE_STATUS status;
291     SERVICE_STATUS_HANDLE handle;
292 douglas 595 HANDLE stop;
293 douglas 594 WSADATA data;
294 douglas 609 std::vector<HANDLE> threads;
295 douglas 594
296 douglas 596 void FingerDaemon();
297 douglas 609 DWORD WINAPI FingerListen(LPVOID server_);
298     DWORD WINAPI FingerDo(LPVOID client_);
299 douglas 596 void FingerStop(int);
300 douglas 595 void WINAPI FingerMain(DWORD argc, LPTSTR *argv);
301     void WINAPI FingerControl(DWORD control);
302 douglas 594
303     int _tmain(int argc, TCHAR *argv[])
304     {
305 douglas 617 SERVICE_TABLE_ENTRY entry[] = { { TEXT("CCSFinger"), LPSERVICE_MAIN_FUNCTION(FingerMain) }, { NULL, NULL } };
306 douglas 594
307 douglas 613 if (!::StartServiceCtrlDispatcher(entry))
308 douglas 594 {
309 douglas 613 DWORD error(::GetLastError());
310 douglas 594
311     switch (error)
312     {
313     case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
314 douglas 595 goto go;
315 douglas 594
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 douglas 595
335     go:
336 douglas 617 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 douglas 613 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
372 douglas 608
373 douglas 613 ::signal(SIGINT, FingerStop);
374 douglas 595
375 douglas 596 try
376     {
377     FingerDaemon();
378     }
379     catch (...) {}
380    
381 douglas 609 return 0;
382 douglas 594 }
383    
384 douglas 596 void FingerDaemon()
385 douglas 594 {
386 douglas 613 ::WSAStartup(MAKEWORD(2, 0), &data);
387 douglas 594
388     //
389    
390 douglas 613 SOCKET server(::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
391 douglas 594 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 douglas 613 ::bind(server, (SOCKADDR *)(&service), sizeof (service));
398     ::listen(server, SOMAXCONN);
399 douglas 594
400 douglas 613 threads.push_back(::CreateThread(NULL, 0, FingerListen, &server, 0, NULL));
401 douglas 608
402 douglas 613 while(::WaitForSingleObject(stop, 1000) != WAIT_OBJECT_0);
403 douglas 594
404     //
405    
406 douglas 613 ::closesocket(server);
407 douglas 608
408 douglas 613 ::WSACleanup();
409 douglas 596 }
410 douglas 594
411 douglas 609 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 douglas 613 threads.push_back(::CreateThread(NULL, 0, FingerDo, &client, 0, NULL));
420 douglas 609 }
421    
422     return 0;
423     }
424    
425     DWORD WINAPI FingerDo(LPVOID client_)
426     {
427     SOCKET &client(*reinterpret_cast<SOCKET *>(client_));
428 douglas 610 char buffer[1024];
429 douglas 613 std::istringstream stream(std::string(buffer, ::recv(client, buffer, sizeof buffer, 0)));
430 douglas 610 std::string line;
431 douglas 609
432 douglas 610 std::getline(stream, line);
433 douglas 609
434 douglas 610 stream.str(line);
435 douglas 609
436 douglas 610 std::getline(stream, line, '\r');
437 douglas 609
438 douglas 610 stream.str(line);
439 douglas 609
440 douglas 610 std::string name;
441     bool full(false);
442 douglas 609
443 douglas 610 while (stream >> line)
444     if (line == "/W")
445     full = true;
446     else
447     name = line;
448 douglas 609
449 douglas 610 std::string finger(name.empty() ? Finger(full) : Finger(name));
450 douglas 609
451 douglas 613 ::send(client, finger.data(), int(finger.size()), 0);
452     ::closesocket(client);
453 douglas 609
454     return 0;
455     }
456    
457 douglas 596 void FingerStop(int)
458     {
459 douglas 613 ::SetEvent(stop);
460 douglas 596 }
461    
462     void WINAPI FingerMain(DWORD argc, LPTSTR *argv)
463     {
464     name = argv[0];
465     handle = RegisterServiceCtrlHandler(name, LPHANDLER_FUNCTION(FingerControl));
466 douglas 613 stop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
467 douglas 596
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 douglas 613 ::SetServiceStatus(handle, &status);
479 douglas 596
480     try
481     {
482     FingerDaemon();
483     }
484     catch (...) {}
485    
486 douglas 595 status.dwCurrentState = SERVICE_STOPPED;
487 douglas 594 status.dwWin32ExitCode = NO_ERROR;
488     status.dwCheckPoint = 0;
489     status.dwWaitHint = 0;
490    
491 douglas 613 ::SetServiceStatus(handle, &status);
492 douglas 594 }
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 douglas 613 ::SetEvent(stop);
503 douglas 594
504     break;
505    
506     case SERVICE_CONTROL_INTERROGATE:
507     break;
508     }
509    
510 douglas 613 ::SetServiceStatus(handle, &status);
511 douglas 594 }