ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/proj/trunk/WinXPFAQPoll/Contactor.cpp
Revision: 117
Committed: 2003-04-01T01:04:41-08:00 (22 years, 2 months ago) by douglas
File size: 11290 byte(s)
Log Message:
Fixed it to work on Unix.

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