ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/proj/trunk/WinXPFAQPoll/Contactor.cpp
Revision: 116
Committed: 2003-04-01T00:43:13-08:00 (22 years, 2 months ago) by douglas
File size: 11086 byte(s)
Log Message:
That is better.

File Contents

# Content
1 /* ============================================================================
2 * Douglas Thrift's Web Contact License
3 *
4 * Copyright (C) 2002, Douglas Thrift. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment:
18 *
19 * "This product includes software developed by Douglas Thrift
20 * (http://computers.douglasthrift.net/webcontact.html)."
21 *
22 * Alternately, this acknowledgment may appear in the software itself, if
23 * and wherever such third-party acknowledgments normally appear.
24 *
25 * 4. The names "Douglas Thrift" and "Douglas Thrift's Web Contact" must not be
26 * used to endorse or promote products derived from this software without
27 * specific prior written permission. For written permission, please visit
28 * http://www.douglasthrift.net/contact.html for contact information.
29 *
30 * 5. Products derived from this software may not be called "Douglas Thrift's
31 * Web Contact", nor may "Douglas Thrift's Web Contact" appear in their
32 * name, without prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
38 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
40 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
43 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 * ============================================================================
45 */
46 // Windows XP FAQ Poll
47 //
48 // Douglas Thrift
49 //
50 // Contactor.cpp
51
52 #include "conio.h"
53 #include "Contactor.h"
54
55 Contactor::Contactor()
56 {
57 loadAccount();
58
59 if (debug)
60 {
61 cerr << "account = {\n host = " << account.getHost() << "\n "
62 << "port = " << account.getPort() << "\n "
63 << "name = " << account.getName() << "\n "
64 << "login = " << account.getLogin() << "\n "
65 << "email = " << account.getEmail() << "\n "
66 << "password = " << string(account.getPassword().length(), '*')
67 << "\n mailbox = " << account.getMailbox() << "\n}\n";
68 }
69
70 session = new IMAPHandler(account.getHost(), account.getPort() == 993);
71
72 string capability = session->capability();
73
74 if (capability.find(" IMAP4rev1") == string::npos)
75 {
76 cerr << program << ": Server does not have IMAP4rev1 capability\n";
77
78 exit(1);
79 }
80
81 if (capability.find(" LOGINDISABLED") != string::npos)
82 {
83 if (capability.find(" STARTTLS") != string::npos)
84 {
85 session->starttls();
86
87 capability = session->capability();
88 }
89 else
90 {
91 cerr << program << ": Server does not allow plain text login\n";
92
93 exit(1);
94 }
95 }
96
97 session->login(account.getLogin() + ' ' + account.getPassword());
98
99 if (!session->successful())
100 {
101 cerr << program << ": Bad login or password\n";
102
103 exit(1);
104 }
105 }
106
107 Contactor::~Contactor()
108 {
109 session->logout();
110
111 delete session;
112 }
113
114 void Contactor::contact(bool nodelete)
115 {
116 this->nodelete = nodelete;
117
118 fixMessages();
119 }
120
121 void Contactor::saveAccount()
122 {
123 ifstream fin("account.dtd");
124
125 bool exist = fin.is_open();
126
127 if (debug) cerr << "exist = " << (exist ? "true" : "false") << "\n";
128
129 if (!exist)
130 {
131 ofstream fout("account.dtd");
132
133 fout << "<!ELEMENT account (host, port?, name, login, name, email, "
134 << "password?, mailbox?)>\n"
135 << "<!ELEMENT host (#PCDATA)>\n"
136 << "<!ELEMENT port (#PCDATA)>\n"
137 << "<!ELEMENT login (#PCDATA)>\n"
138 << "<!ELEMENT name (#PCDATA)>\n"
139 << "<!ELEMENT email (#PCDATA)>\n"
140 << "<!ELEMENT password (#PCDATA)>\n"
141 << "<!ELEMENT mailbox (#PCDATA)>\n";
142
143 fout.close();
144 }
145 else
146 {
147 fin.close();
148 }
149
150 Account account;
151
152 cout << "Host: ";
153
154 char* host = new char[1024];
155 cin.getline(host, 1024);
156
157 account.setHost(host);
158 delete [] host;
159
160 cout << "Port: ";
161
162 char* port = new char[1024];
163 cin.getline(port, 1024);
164
165 if (strlen(port) > 0) account.setPort(strtoul(port, 0, 0));
166 delete [] port;
167
168 cout << "Login: ";
169
170 char* login = new char[1024];
171 cin.getline(login, 1024);
172
173 account.setLogin(login);
174 delete [] login;
175
176 cout << "Name: ";
177
178 char* name = new char[1024];
179 cin.getline(name, 1024);
180
181 account.setName(name);
182 delete [] name;
183
184 cout << "Email: ";
185
186 char* email = new char[1024];
187 cin.getline(email, 1024);
188
189 account.setEmail(email);
190 delete [] email;
191
192 cout << "Password: " << flush;
193
194 string password;
195
196 char letter;
197 do
198 {
199 letter = _getch();
200
201 if (letter == '\r' || letter == '\n')
202 {
203 cout << "\n";
204 }
205 else if (letter == '\b' && password.length() > 0)
206 {
207 cout << "\b \b";
208 password.erase(password.length() - 1);
209 }
210 else if (letter == '\b')
211 {
212 // do nothing
213 }
214 else
215 {
216 cout << '*';
217 password += letter;
218 }
219 }
220 while (letter != '\r' && letter != '\n');
221
222 account.setPassword(password);
223
224 cout << "Mailbox: ";
225
226 char* mailbox = new char[1024];
227 cin.getline(mailbox, 1024);
228
229 if (strlen(mailbox) > 0) account.setMailbox(mailbox);
230 delete [] mailbox;
231
232 ofstream fout("account.xml");
233
234 fout << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>"
235 << "\n<!DOCTYPE account SYSTEM \"account.dtd\">\n" << account << "\n";
236
237 fout.close();
238 }
239
240 void Contactor::loadAccount()
241 {
242 const string XMLTYPE = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" stan"
243 + string("dalone=\"no\"?>");
244 const string DOCTYPE = "<!DOCTYPE account SYSTEM \"account.dtd\">";
245
246 ifstream fin("account.xml");
247
248 if (!fin.is_open())
249 {
250 cerr << program << ": Could not open account file: account.xml\n";
251
252 exit(1);
253 }
254
255 string line;
256
257 getline(fin, line);
258 if (line != XMLTYPE)
259 {
260 cerr << program << ": Invalid XML version declaration: account.xml\n";
261
262 fin.close();
263 exit(1);
264 }
265
266 getline(fin, line);
267 if (line != DOCTYPE)
268 {
269 cerr << program << ": Invalid XML doctype: account.xml\n";
270
271 fin.close();
272 exit(1);
273 }
274
275 fin >> account;
276
277 if (account.getPassword() == "")
278 {
279 cout << "Password: " << flush;
280
281 string password;
282
283 char letter;
284 do
285 {
286 letter = _getch();
287
288 if (letter == '\r' || letter == '\n')
289 {
290 cout << "\n";
291 }
292 else if (letter == '\b' && password.length() > 0)
293 {
294 cout << "\b \b";
295 password.erase(password.length() - 1);
296 }
297 else if (letter == '\b')
298 {
299 // do nothing
300 }
301 else
302 {
303 cout << '*';
304 password += letter;
305 }
306 }
307 while (letter != '\r' && letter != '\n');
308
309 account.setPassword(password);
310 }
311 }
312
313 void Contactor::fixMessages()
314 {
315 session->select("\"" + account.getMailbox() + "\"");
316
317 if (!session->successful())
318 {
319 cerr << program << ": Bad mailbox name: " << account.getMailbox()
320 << "\n";
321 exit(1);
322 }
323
324 string search = session->search("ALL FROM \"" + account.getEmail() + "\" "
325 + "BODY \"name = \" BODY \"email = \" BODY \"subject = \" "
326 + "BODY \"comments = \" BODY \"login = \" BODY \"REMOTE_HOST: \"");
327
328 if (search.find("* SEARCH ") == 0)
329 {
330 messages;
331
332 unsigned begin = 9;
333 do
334 {
335 unsigned end = search.find_first_of(" \r\n", begin);
336
337 string message = search.substr(begin, end - begin);
338
339 if (message != "")
340 {
341 if (debug) cerr << "message = " << message << "\n";
342 messages.push(message);
343 }
344
345 if (end == string::npos) break;
346 begin = end + 1;
347 }
348 while (begin < search.length());
349
350 if (!messages.empty())
351 {
352 string message = messages.front();
353 messages.pop();
354
355 fixMessage(message);
356 }
357 }
358
359 if (!nodelete)
360 {
361 session->expunge();
362 }
363 }
364
365 void Contactor::fixMessage(const string& number)
366 {
367 cout << "Fixing message: " << number << "..." << flush;
368
369 string header = session->fetch(number + " BODY.PEEK[HEADER]");
370 string text = session->fetch(number + " BODY.PEEK[TEXT]");
371
372 Message message;
373 if (parseHeader(header, message) && parseText(text, message))
374 {
375 message.setMessage();
376
377 session->append("\"" + account.getMailbox() + "\" \""
378 + message.getImapDate() + "\" {" + message.getLength() + '}',
379 message.getMessage());
380
381 if (session->successful())
382 {
383 session->store(number + " +FLAGS (\\Deleted)");
384 cout << "done.\n";
385 }
386 else
387 {
388 cerr << program << ": Error storing message: " << number << "\n";
389 exit(1);
390 }
391 }
392 else
393 {
394 cout << "cancelled.\n";
395 }
396
397 if (!messages.empty())
398 {
399 string next = messages.front();
400 messages.pop();
401
402 fixMessage(next);
403 }
404 }
405
406 bool Contactor::parseHeader(const string& header, Message& message)
407 {
408 bool answer = false;
409
410 unsigned field = header.find("Date: ") + 6;
411 unsigned end = header.find("\r\n", field);
412
413 string date = header.substr(field, end - field);
414 message.setDate(date);
415
416 message.setTo("\"" + account.getName() + "\" <" + account.getEmail()
417 + '>');
418
419 field = header.find("Subject: ") + 9;
420 end = header.find("\r\n", field);
421
422 string subject = header.substr(field, end - field);
423 message.setSubject(subject);
424
425 vector<string> recieved;
426 unsigned begin = 0;
427 do
428 {
429 field = header.find("Received: ", begin) + 10;
430 end = header.find("\r\n", field);
431
432 if (field - 10 == string::npos) break;
433
434 string value = header.substr(field, end - field);
435
436 while (header[end + 2] == ' ' || header[end + 2] == ' ')
437 {
438 begin = end + 2;
439 end = header.find("\r\n", begin);
440
441 value += "\r\n" + header.substr(begin, end - begin);
442 }
443
444 recieved.push_back(value);
445
446 if (end == string::npos) break;
447 begin = end + 1;
448 }
449 while (begin < header.length());
450 message.setRecieved(recieved);
451
452 if (date != "" && recieved.size() > 0) answer = true;
453
454 return answer;
455 }
456
457 bool Contactor::parseText(const string& text, Message& message)
458 {
459 bool answer = false;
460
461 unsigned field = text.find("name = ") + 7;
462 unsigned end = text.find("\r\nemail = ", field);
463
464 string name = text.substr(field, end - field);
465
466 field = end + 10;
467 end = text.find("\r\nsubject = ", field);
468
469 string email = text.substr(field, end - field);
470
471 message.setFrom("\"" + name + "\" <" + email + '>');
472
473 field = end + 12;
474 end = text.find("\r\ncomments = ", field);
475
476 string subject = text.substr(field, end - field);
477
478 field = end + 13;
479 end = text.find("\r\nlogin = ", field);
480
481 string comments = text.substr(field, end - field);
482 message.setText(comments);
483
484 field = text.find("\r\nREMOTE_HOST: ", end) + 15;
485 end = text.find("\r\n", field);
486
487 string host = text.substr(field, end - field);
488 message.setHost(host);
489
490 if (subject == message.getSubject()) answer = true;
491
492 return answer;
493 }