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

# 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 "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 douglas 116 session = new IMAPHandler(account.getHost(), account.getPort() == 993);
71 douglas 104
72 douglas 116 string capability = session->capability();
73    
74     if (capability.find(" IMAP4rev1") == string::npos)
75 douglas 104 {
76     cerr << program << ": Server does not have IMAP4rev1 capability\n";
77    
78     exit(1);
79     }
80 douglas 116
81     if (capability.find(" LOGINDISABLED") != string::npos)
82     {
83     if (capability.find(" STARTTLS") != string::npos)
84     {
85     session->starttls();
86 douglas 104
87 douglas 116 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 douglas 104 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     }