ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/proj/trunk/WinXPFAQPoll/Contactor.cpp
Revision: 218
Committed: 2003-07-23T18:31:13-07:00 (21 years, 11 months ago) by douglas
File size: 11341 byte(s)
Log Message:
Fixed it to work with web host's IMAP server, phooey!

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 douglas 218 if (capability.find(" IMAP4rev1") == string::npos &&
81     capability.find(" IMAP4REV1") == string::npos)
82 douglas 104 {
83     cerr << program << ": Server does not have IMAP4rev1 capability\n";
84    
85     exit(1);
86     }
87 douglas 116
88     if (capability.find(" LOGINDISABLED") != string::npos)
89     {
90     if (capability.find(" STARTTLS") != string::npos)
91     {
92     session->starttls();
93 douglas 104
94 douglas 116 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 douglas 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 douglas 117 #ifdef _WIN32
200 douglas 104 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 douglas 117 #else
230     string password = getpass("Password: ");
231     #endif
232 douglas 104
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 douglas 117 #ifdef _WIN32
291 douglas 104 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 douglas 117 #else
321     string password = getpass("Password: ");
322     #endif
323 douglas 104
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     }