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

# User Rev Content
1 douglas 104 /* ============================================================================
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 douglas 117 #ifdef _WIN32
55     #include "conio.h"
56     #else
57     #include <pwd.h>
58     #include <unistd.h>
59     #endif
60    
61 douglas 104 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 douglas 116 session = new IMAPHandler(account.getHost(), account.getPort() == 993);
77 douglas 104
78 douglas 116 string capability = session->capability();
79    
80     if (capability.find(" IMAP4rev1") == string::npos)
81 douglas 104 {
82     cerr << program << ": Server does not have IMAP4rev1 capability\n";
83    
84     exit(1);
85     }
86 douglas 116
87     if (capability.find(" LOGINDISABLED") != string::npos)
88     {
89     if (capability.find(" STARTTLS") != string::npos)
90     {
91     session->starttls();
92 douglas 104
93 douglas 116 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 douglas 104 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 douglas 117 #ifdef _WIN32
199 douglas 104 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 douglas 117 #else
229     string password = getpass("Password: ");
230     #endif
231 douglas 104
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 douglas 117 #ifdef _WIN32
290 douglas 104 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 douglas 117 #else
320     string password = getpass("Password: ");
321     #endif
322 douglas 104
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     }