ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/proj/WinXPFAQPoll/Contactor.cpp
Revision: 368
Committed: 2008-08-23T02:44:00-07:00 (14 years, 5 months ago) by douglas
File size: 11341 byte(s)
Log Message:
Rearranged everything else.

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 "Contactor.h"
53
54 #ifdef _WIN32
55 #include "conio.h"
56 #else
57 #include <pwd.h>
58 #include <unistd.h>
59 #endif
60
61 Contactor::Contactor()
62 {
63 loadAccount();
64
65 if (debug)
66 {
67 cerr << "account = {\n host = " << account.getHost() << "\n "
68 << "port = " << account.getPort() << "\n "
69 << "name = " << account.getName() << "\n "
70 << "login = " << account.getLogin() << "\n "
71 << "email = " << account.getEmail() << "\n "
72 << "password = " << string(account.getPassword().length(), '*')
73 << "\n mailbox = " << account.getMailbox() << "\n}\n";
74 }
75
76 session = new IMAPHandler(account.getHost(), account.getPort() == 993);
77
78 string capability = session->capability();
79
80 if (capability.find(" IMAP4rev1") == string::npos &&
81 capability.find(" IMAP4REV1") == string::npos)
82 {
83 cerr << program << ": Server does not have IMAP4rev1 capability\n";
84
85 exit(1);
86 }
87
88 if (capability.find(" LOGINDISABLED") != string::npos)
89 {
90 if (capability.find(" STARTTLS") != string::npos)
91 {
92 session->starttls();
93
94 capability = session->capability();
95 }
96 else
97 {
98 cerr << program << ": Server does not allow plain text login\n";
99
100 exit(1);
101 }
102 }
103
104 session->login(account.getLogin() + ' ' + account.getPassword());
105
106 if (!session->successful())
107 {
108 cerr << program << ": Bad login or password\n";
109
110 exit(1);
111 }
112 }
113
114 Contactor::~Contactor()
115 {
116 session->logout();
117
118 delete session;
119 }
120
121 void Contactor::contact(bool nodelete)
122 {
123 this->nodelete = nodelete;
124
125 fixMessages();
126 }
127
128 void Contactor::saveAccount()
129 {
130 ifstream fin("account.dtd");
131
132 bool exist = fin.is_open();
133
134 if (debug) cerr << "exist = " << (exist ? "true" : "false") << "\n";
135
136 if (!exist)
137 {
138 ofstream fout("account.dtd");
139
140 fout << "<!ELEMENT account (host, port?, name, login, name, email, "
141 << "password?, mailbox?)>\n"
142 << "<!ELEMENT host (#PCDATA)>\n"
143 << "<!ELEMENT port (#PCDATA)>\n"
144 << "<!ELEMENT login (#PCDATA)>\n"
145 << "<!ELEMENT name (#PCDATA)>\n"
146 << "<!ELEMENT email (#PCDATA)>\n"
147 << "<!ELEMENT password (#PCDATA)>\n"
148 << "<!ELEMENT mailbox (#PCDATA)>\n";
149
150 fout.close();
151 }
152 else
153 {
154 fin.close();
155 }
156
157 Account account;
158
159 cout << "Host: ";
160
161 char* host = new char[1024];
162 cin.getline(host, 1024);
163
164 account.setHost(host);
165 delete [] host;
166
167 cout << "Port: ";
168
169 char* port = new char[1024];
170 cin.getline(port, 1024);
171
172 if (strlen(port) > 0) account.setPort(strtoul(port, 0, 0));
173 delete [] port;
174
175 cout << "Login: ";
176
177 char* login = new char[1024];
178 cin.getline(login, 1024);
179
180 account.setLogin(login);
181 delete [] login;
182
183 cout << "Name: ";
184
185 char* name = new char[1024];
186 cin.getline(name, 1024);
187
188 account.setName(name);
189 delete [] name;
190
191 cout << "Email: ";
192
193 char* email = new char[1024];
194 cin.getline(email, 1024);
195
196 account.setEmail(email);
197 delete [] email;
198
199 #ifdef _WIN32
200 cout << "Password: " << flush;
201
202 string password;
203
204 char letter;
205 do
206 {
207 letter = _getch();
208
209 if (letter == '\r' || letter == '\n')
210 {
211 cout << "\n";
212 }
213 else if (letter == '\b' && password.length() > 0)
214 {
215 cout << "\b \b";
216 password.erase(password.length() - 1);
217 }
218 else if (letter == '\b')
219 {
220 // do nothing
221 }
222 else
223 {
224 cout << '*';
225 password += letter;
226 }
227 }
228 while (letter != '\r' && letter != '\n');
229 #else
230 string password = getpass("Password: ");
231 #endif
232
233 account.setPassword(password);
234
235 cout << "Mailbox: ";
236
237 char* mailbox = new char[1024];
238 cin.getline(mailbox, 1024);
239
240 if (strlen(mailbox) > 0) account.setMailbox(mailbox);
241 delete [] mailbox;
242
243 ofstream fout("account.xml");
244
245 fout << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>"
246 << "\n<!DOCTYPE account SYSTEM \"account.dtd\">\n" << account << "\n";
247
248 fout.close();
249 }
250
251 void Contactor::loadAccount()
252 {
253 const string XMLTYPE = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" stan"
254 + string("dalone=\"no\"?>");
255 const string DOCTYPE = "<!DOCTYPE account SYSTEM \"account.dtd\">";
256
257 ifstream fin("account.xml");
258
259 if (!fin.is_open())
260 {
261 cerr << program << ": Could not open account file: account.xml\n";
262
263 exit(1);
264 }
265
266 string line;
267
268 getline(fin, line);
269 if (line != XMLTYPE)
270 {
271 cerr << program << ": Invalid XML version declaration: account.xml\n";
272
273 fin.close();
274 exit(1);
275 }
276
277 getline(fin, line);
278 if (line != DOCTYPE)
279 {
280 cerr << program << ": Invalid XML doctype: account.xml\n";
281
282 fin.close();
283 exit(1);
284 }
285
286 fin >> account;
287
288 if (account.getPassword() == "")
289 {
290 #ifdef _WIN32
291 cout << "Password: " << flush;
292
293 string password;
294
295 char letter;
296 do
297 {
298 letter = _getch();
299
300 if (letter == '\r' || letter == '\n')
301 {
302 cout << "\n";
303 }
304 else if (letter == '\b' && password.length() > 0)
305 {
306 cout << "\b \b";
307 password.erase(password.length() - 1);
308 }
309 else if (letter == '\b')
310 {
311 // do nothing
312 }
313 else
314 {
315 cout << '*';
316 password += letter;
317 }
318 }
319 while (letter != '\r' && letter != '\n');
320 #else
321 string password = getpass("Password: ");
322 #endif
323
324 account.setPassword(password);
325 }
326 }
327
328 void Contactor::fixMessages()
329 {
330 session->select("\"" + account.getMailbox() + "\"");
331
332 if (!session->successful())
333 {
334 cerr << program << ": Bad mailbox name: " << account.getMailbox()
335 << "\n";
336 exit(1);
337 }
338
339 string search = session->search("ALL FROM \"" + account.getEmail() + "\" "
340 + "BODY \"name = \" BODY \"email = \" BODY \"subject = \" "
341 + "BODY \"comments = \" BODY \"login = \" BODY \"REMOTE_HOST: \"");
342
343 if (search.find("* SEARCH ") == 0)
344 {
345 messages;
346
347 unsigned begin = 9;
348 do
349 {
350 unsigned end = search.find_first_of(" \r\n", begin);
351
352 string message = search.substr(begin, end - begin);
353
354 if (message != "")
355 {
356 if (debug) cerr << "message = " << message << "\n";
357 messages.push(message);
358 }
359
360 if (end == string::npos) break;
361 begin = end + 1;
362 }
363 while (begin < search.length());
364
365 if (!messages.empty())
366 {
367 string message = messages.front();
368 messages.pop();
369
370 fixMessage(message);
371 }
372 }
373
374 if (!nodelete)
375 {
376 session->expunge();
377 }
378 }
379
380 void Contactor::fixMessage(const string& number)
381 {
382 cout << "Fixing message: " << number << "..." << flush;
383
384 string header = session->fetch(number + " BODY.PEEK[HEADER]");
385 string text = session->fetch(number + " BODY.PEEK[TEXT]");
386
387 Message message;
388 if (parseHeader(header, message) && parseText(text, message))
389 {
390 message.setMessage();
391
392 session->append("\"" + account.getMailbox() + "\" \""
393 + message.getImapDate() + "\" {" + message.getLength() + '}',
394 message.getMessage());
395
396 if (session->successful())
397 {
398 session->store(number + " +FLAGS (\\Deleted)");
399 cout << "done.\n";
400 }
401 else
402 {
403 cerr << program << ": Error storing message: " << number << "\n";
404 exit(1);
405 }
406 }
407 else
408 {
409 cout << "cancelled.\n";
410 }
411
412 if (!messages.empty())
413 {
414 string next = messages.front();
415 messages.pop();
416
417 fixMessage(next);
418 }
419 }
420
421 bool Contactor::parseHeader(const string& header, Message& message)
422 {
423 bool answer = false;
424
425 unsigned field = header.find("Date: ") + 6;
426 unsigned end = header.find("\r\n", field);
427
428 string date = header.substr(field, end - field);
429 message.setDate(date);
430
431 message.setTo("\"" + account.getName() + "\" <" + account.getEmail()
432 + '>');
433
434 field = header.find("Subject: ") + 9;
435 end = header.find("\r\n", field);
436
437 string subject = header.substr(field, end - field);
438 message.setSubject(subject);
439
440 vector<string> recieved;
441 unsigned begin = 0;
442 do
443 {
444 field = header.find("Received: ", begin) + 10;
445 end = header.find("\r\n", field);
446
447 if (field - 10 == string::npos) break;
448
449 string value = header.substr(field, end - field);
450
451 while (header[end + 2] == ' ' || header[end + 2] == ' ')
452 {
453 begin = end + 2;
454 end = header.find("\r\n", begin);
455
456 value += "\r\n" + header.substr(begin, end - begin);
457 }
458
459 recieved.push_back(value);
460
461 if (end == string::npos) break;
462 begin = end + 1;
463 }
464 while (begin < header.length());
465 message.setRecieved(recieved);
466
467 if (date != "" && recieved.size() > 0) answer = true;
468
469 return answer;
470 }
471
472 bool Contactor::parseText(const string& text, Message& message)
473 {
474 bool answer = false;
475
476 unsigned field = text.find("name = ") + 7;
477 unsigned end = text.find("\r\nemail = ", field);
478
479 string name = text.substr(field, end - field);
480
481 field = end + 10;
482 end = text.find("\r\nsubject = ", field);
483
484 string email = text.substr(field, end - field);
485
486 message.setFrom("\"" + name + "\" <" + email + '>');
487
488 field = end + 12;
489 end = text.find("\r\ncomments = ", field);
490
491 string subject = text.substr(field, end - field);
492
493 field = end + 13;
494 end = text.find("\r\nlogin = ", field);
495
496 string comments = text.substr(field, end - field);
497 message.setText(comments);
498
499 field = text.find("\r\nREMOTE_HOST: ", end) + 15;
500 end = text.find("\r\n", field);
501
502 string host = text.substr(field, end - field);
503 message.setHost(host);
504
505 if (subject == message.getSubject()) answer = true;
506
507 return answer;
508 }