Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/src/main.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 132 - (show annotations) (download) (as text)
Tue Sep 13 17:40:57 2016 UTC (7 years, 6 months ago) by z0rac
File MIME type: text/x-c++src
File size: 12066 byte(s)


1 /*
2 * Copyright (C) 2009-2016 TSUBAKIMOTO Hiroya <z0rac@users.sourceforge.jp>
3 *
4 * This software comes with ABSOLUTELY NO WARRANTY; for details of
5 * the license terms, see the LICENSE.txt file included with the program.
6 */
7 #include "define.h"
8 #include "mailbox.h"
9 #include "setting.h"
10 #include "winsock.h"
11 #include "win32.h"
12 #include "window.h"
13 #include <algorithm>
14 #include <cassert>
15 #include <imagehlp.h>
16 #include <shlobj.h>
17 #include <shlwapi.h>
18
19 #ifdef _DEBUG
20 #include <iostream>
21 #define DBG(s) s
22 #define LOG(s) (cout << win32::time(time(NULL)) << "|" << s)
23 #else
24 #define DBG(s)
25 #define LOG(s)
26 #endif
27
28 extern window* mascot();
29 extern window* summary(const mailbox*);
30
31 // model - main model
32 namespace {
33 class model : public window::timer {
34 static model* _model;
35 struct mbox : public mailbox {
36 unsigned period;
37 unsigned next;
38 string sound;
39 mbox* fetch;
40 mbox(const string& name)
41 : mailbox(name), period(0), next(0) {}
42 };
43 mailbox* _mailboxes;
44 void _release();
45 void wakeup(window& source) { fetch(source, false); }
46 public:
47 model();
48 ~model() { _release(); }
49 const mailbox* mailboxes() const { return _mailboxes; }
50 void cache();
51 model& fetch(window& source, bool force = true);
52 bool fetching() const { return _fetching != 0; }
53 private:
54 // the class to control fetching
55 win32::mex _key;
56 DWORD _last;
57 unsigned _fetching;
58 list<mailbox*> _fetched;
59 int _summary;
60 HANDLE _idle;
61 void _done(mbox& mb);
62 static void _thread(void* param);
63 };
64 model* model::_model = NULL;
65 }
66
67 model::model()
68 : _mailboxes(NULL), _last(GetTickCount()), _fetching(0), _summary(0),
69 _idle(win32::valid(CreateEvent(NULL, TRUE, TRUE, NULL)))
70 {
71 try {
72 mailbox* last = NULL;
73 list<string> mbs = setting::mailboxes();
74 for (list<string>::iterator p = mbs.begin(); p != mbs.end(); ++p) {
75 string name = *p;
76 LOG("Load mailbox [" << name << "]" << endl);
77 setting s = setting::mailbox(name);
78 mbox* mb = new mbox(name);
79 unique_ptr<mbox> hold(mb);
80 int ip, verify;
81 s["ip"](ip = 0);
82 s["verify"](verify = 3);
83 mb->uripasswd(s["uri"], s.cipher("passwd"))
84 .domain(ip == 4 ? AF_INET : ip == 6 ? AF_INET6 : AF_UNSPEC)
85 .verify(verify);
86 int period;
87 s["period"](period = 15);
88 s["sound"].sep(0)(mb->sound);
89 mb->period = period > 0 ? period * 60000U : 0;
90 list<string> cache = setting::cache(mb->uristr());
91 mb->ignore(cache);
92 hold.release();
93 last = last ? last->next(mb) : (_mailboxes = mb);
94 }
95 setting::cacheclear();
96 setting::preferences()["summary"]()(_summary);
97 } catch (...) {
98 _release();
99 throw;
100 }
101 _model = this;
102 }
103
104 void
105 model::_release()
106 {
107 cache();
108 for (mailbox* p = _mailboxes; p;) {
109 mailbox* next = p->next();
110 delete p;
111 p = next;
112 }
113 CloseHandle(_idle);
114 }
115
116 void
117 model::cache()
118 {
119 WaitForSingleObject(_idle, INFINITE);
120 for (mailbox* p = _mailboxes; p; p = p->next()) {
121 setting::cache(p->uristr(), p->ignore());
122 }
123 }
124
125 model&
126 model::fetch(window& source, bool force)
127 {
128 win32::mex::trylock lock(_key);
129 if (!lock) {
130 source.settimer(*this, 1); // delay to fetch.
131 return *this;
132 }
133 LOG("Fetch mails..." << endl);
134 unsigned next = 0;
135 mbox* fetch = NULL;
136 DWORD elapse = GetTickCount() - _last;
137 _last += elapse;
138 for (mailbox* p = _mailboxes; p; p = p->next()) {
139 mbox* mb = static_cast<mbox*>(p);
140 if (!mb->period) continue; // No fetching by the setting.
141 if (force || mb->next <= elapse) {
142 unsigned late = force ? 0 : (elapse - mb->next) % mb->period;
143 mb->next = mb->period - late;
144 mb->fetch = fetch, fetch = mb;
145 } else {
146 mb->next -= elapse;
147 }
148 if (!next || next > mb->next) next = mb->next;
149 }
150 source.settimer(*this, next);
151 for (; fetch; fetch = fetch->fetch) {
152 list<mailbox*>::iterator end = _fetched.end();
153 if (find(_fetched.begin(), end, fetch) == end) {
154 CloseHandle(win32::thread(_thread, (void*)fetch));
155 if (_fetching++ == 0) {
156 ResetEvent(_idle);
157 window::broadcast(WM_APP, 0, 0);
158 }
159 _fetched.push_front(fetch);
160 }
161 }
162 return *this;
163 }
164
165 void
166 model::_done(mbox& mb)
167 {
168 if (mb.recent() > 0 && !mb.sound.empty()) {
169 LOG("Sound: " << mb.sound << endl);
170 const char* name = mb.sound.c_str();
171 PlaySound(name, NULL, SND_NODEFAULT | SND_NOWAIT | SND_ASYNC|
172 (*PathFindExtension(name) ? SND_FILENAME : SND_ALIAS));
173 }
174
175 win32::mex::lock lock(_key);
176 if (--_fetching) return;
177
178 LOG("Done all fetching." << endl);
179 size_t recent = 0;
180 size_t unseen = 0;
181 for (const mailbox* p = _mailboxes; p; p = p->next()) {
182 int n = p->recent();
183 if (n > 0) recent += n;
184 unseen += p->mails().size();
185 }
186 window::broadcast(WM_APP, MAKEWPARAM(recent, unseen), LPARAM(&_fetched));
187 if (recent && _summary) {
188 list<mailbox*>::const_iterator p = _fetched.begin();
189 while (p != _fetched.end() && (*p)->recent() <= 0) ++p;
190 if (p != _fetched.end()) {
191 window::broadcast(WM_COMMAND, MAKEWPARAM(0, ID_MENU_SUMMARY), 0);
192 }
193 }
194 _fetched.clear();
195 LOG("***** HEAP SIZE [" << win32::cheapsize() << ", "
196 << win32::heapsize() << "] *****" << endl);
197 SetEvent(_idle);
198 }
199
200 void
201 model::_thread(void* param)
202 {
203 mbox& mb = *reinterpret_cast<mbox*>(param);
204 LOG("Start thread [" << mb.name() << "]." << endl);
205 try {
206 mb.fetchmail();
207 } catch (const exception& DBG(e)) {
208 LOG(e.what() << endl);
209 } catch (...) {
210 LOG("Unknown exception." << endl);
211 }
212 LOG("End thread [" << mb.name() << "]." << endl);
213 _model->_done(mb);
214 }
215
216 namespace { // misc. functions
217 bool appendix(const char* file, char* path)
218 {
219 return GetModuleFileName(NULL, path, MAX_PATH) < MAX_PATH &&
220 PathRemoveFileSpec(path) && PathAppend(path, file) &&
221 PathFileExists(path);
222 }
223
224 bool shell(const string& cmd)
225 {
226 HANDLE h = win32::shell(cmd, SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT);
227 if (!h) return false;
228 WaitForSingleObject(h, INFINITE);
229 CloseHandle(h);
230 return true;
231 }
232
233 bool rundll(const char* arg)
234 {
235 char s[MAX_PATH];
236 if (!appendix("extend.dll", s)) return false;
237 win32::dll extend(s);
238 if (!extend) return false;
239 typedef void (*settingdlg)(HWND, HINSTANCE, LPCSTR, int);
240 settingdlg fun = settingdlg(extend("settingdlg", NULL));
241 if (!fun) return false;
242 fun(NULL, win32::exe, arg, SW_NORMAL);
243 return true;
244 }
245 }
246
247 #if USE_REG
248 /** repository - registory added some features.
249 */
250 #define REG_KEY "Software\\GNU\\" APP_NAME
251 namespace {
252 class repository : public registory {
253 public:
254 repository() : registory(REG_KEY) {}
255 bool edit();
256 };
257 }
258
259 bool
260 repository::edit()
261 {
262 LOG("Edit setting." << endl);
263
264 struct event {
265 HANDLE h;
266 event() : h(win32::valid(CreateEvent(NULL, FALSE, FALSE, NULL))) {}
267 ~event() { CloseHandle(h); }
268 } event;
269
270 struct key {
271 HKEY h;
272 key(const char* key)
273 { if (RegOpenKeyEx(HKEY_CURRENT_USER, key,
274 0, KEY_NOTIFY, &h) != ERROR_SUCCESS) throw win32::error(); }
275 ~key() { RegCloseKey(h); }
276 } key(REG_KEY);
277
278 if (RegNotifyChangeKeyValue(key.h, TRUE,
279 REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
280 event.h, TRUE) != ERROR_SUCCESS) throw win32::error();
281 return rundll(REG_KEY) && WaitForSingleObject(event.h, 0) == WAIT_OBJECT_0;
282 }
283 #else // !USE_REG
284 /** repository - profile added some features.
285 */
286 #define INI_FILE APP_NAME ".ini"
287 namespace {
288 class repository : public profile {
289 static string _path;
290 static const char* _prepare();
291 public:
292 repository() : profile(_prepare()) {}
293 bool edit();
294 };
295 string repository::_path;
296 }
297
298 const char*
299 repository::_prepare()
300 {
301 char path[MAX_PATH];
302 if (appendix(INI_FILE, path) ||
303 (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
304 NULL, SHGFP_TYPE_CURRENT, path) == 0 &&
305 PathAppend(path, APP_NAME "\\" INI_FILE) &&
306 MakeSureDirectoryPathExists(path))) {
307 LOG("Using the setting file: " << path << endl);
308 _path = path;
309 HANDLE h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0,
310 NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
311 if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
312 return _path.c_str();
313 }
314 return NULL;
315 }
316
317 bool
318 repository::edit()
319 {
320 LOG("Edit setting." << endl);
321 if (_path.empty()) return false;
322
323 WritePrivateProfileString(NULL, NULL, NULL, _path.c_str()); // flush entries.
324 HANDLE fh = CreateFile(_path.c_str(), GENERIC_READ,
325 FILE_SHARE_READ | FILE_SHARE_WRITE,
326 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
327 if (fh == INVALID_HANDLE_VALUE) throw win32::error();
328
329 FILETIME before = { 0 };
330 GetFileTime(fh, NULL, NULL, &before);
331 FILETIME after = before;
332 (rundll(_path.c_str()) || shell('"' + _path + '"')) &&
333 GetFileTime(fh, NULL, NULL, &after);
334 CloseHandle(fh);
335
336 return CompareFileTime(&before, &after) != 0;
337 }
338 #endif // !USE_REG
339
340 namespace cmd {
341 struct fetch : public window::command {
342 model& _model;
343 fetch(model& model) : window::command(-265), _model(model) {}
344 void execute(window& source) { _model.fetch(source); }
345 UINT state(window&) { return _model.fetching() ? MFS_DISABLED : 0; }
346 };
347
348 struct summary : public window::command {
349 model& _model;
350 unique_ptr<window> _summary;
351 summary(model& model) : window::command(-281), _model(model) {}
352 void execute(window&)
353 {
354 LOG("Open the summary window." << endl);
355 _summary.reset(); // to save preferences
356 _summary.reset(::summary(_model.mailboxes()));
357 }
358 UINT state(window&) { return _model.fetching() ? MFS_DISABLED : 0; }
359 };
360
361 struct setting : public window::command {
362 repository& _rep;
363 DWORD _tid;
364 HANDLE _thread;
365 setting(repository& rep) : window::command(-274),
366 _rep(rep), _tid(GetCurrentThreadId()), _thread(NULL) {}
367 ~setting() { _thread && WaitForSingleObject(_thread, INFINITE); }
368 void execute(window&) { if (!busy()) _thread = win32::thread(edit, (void*)this); }
369 UINT state(window&) { return busy() ? MFS_DISABLED : 0; }
370 static void edit(void* param)
371 {
372 setting& self = *reinterpret_cast<setting*>(param);
373 if (self._rep.edit()) PostThreadMessage(self._tid, WM_QUIT, 1, 0);
374 }
375 bool busy()
376 {
377 if (_thread && WaitForSingleObject(_thread, 0) == WAIT_OBJECT_0) {
378 CloseHandle(_thread);
379 _thread = NULL;
380 }
381 return _thread != NULL;
382 }
383 };
384
385 struct exit : public window::command {
386 exit() : window::command(-28) {}
387 void execute(window&) { LOG("Exit." << endl); PostQuitMessage(0); }
388 };
389
390 struct logoff : public window::command {
391 model& _model;
392 logoff(model& model) : _model(model) {}
393 void execute(window&) { _model.cache(); }
394 };
395 }
396
397 /*
398 * WinMain - main function
399 */
400 #if defined(_DEBUG) && defined(_MSC_VER)
401 int main()
402 #else
403 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
404 #endif
405 {
406 try {
407 static win32 befoo("befoo:79585F30-DD15-446C-B414-152D31324970");
408 static winsock winsock;
409 static repository rep;
410 int delay;
411 setting::preferences()["delay"](delay = 0);
412 for (int qc = 1; qc > 0; delay = 0) {
413 unique_ptr<window> w(mascot());
414 unique_ptr<model> m(new model);
415 w->addcmd(ID_MENU_FETCH, new cmd::fetch(*m));
416 w->addcmd(ID_MENU_SUMMARY, new cmd::summary(*m));
417 w->addcmd(ID_MENU_SETTINGS, new cmd::setting(rep));
418 w->addcmd(ID_MENU_EXIT, new cmd::exit);
419 w->addcmd(ID_EVENT_LOGOFF, new cmd::logoff(*m));
420 w->settimer(*m, delay > 0 ? delay * 1000 : 1);
421 qc = window::eventloop();
422 }
423 return 0;
424 } catch (...) {}
425 return -1;
426 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26